SQL Server utilizza sempre la combinazione di operatori di suddivisione, ordinamento e compressione durante la gestione di un indice univoco come parte di un aggiornamento che influenza (o potrebbe influire) su più di una riga.
Lavorando sull'esempio della domanda, potremmo scrivere l'aggiornamento come un aggiornamento a riga singola separato per ciascuna delle quattro righe presenti:
-- Per row updates
UPDATE dbo.Banana SET pk = 2 WHERE pk = 1;
UPDATE dbo.Banana SET pk = 3 WHERE pk = 2;
UPDATE dbo.Banana SET pk = 4 WHERE pk = 3;
UPDATE dbo.Banana SET pk = 5 WHERE pk = 4;
Il problema è che la prima istruzione fallirebbe, poiché cambia pk
da 1 a 2, e c'è già una riga in cui pk
= 2. Il motore di archiviazione di SQL Server richiede che gli indici univoci rimangano univoci in ogni fase dell'elaborazione, anche all'interno di una singola istruzione . Questo è il problema risolto da Split, Sort e Collapse.
Diviso
Il primo passo è quello di dividere ogni istruzione di aggiornamento in una cancellazione seguita da un inserimento:
DELETE dbo.Banana WHERE pk = 1;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');
DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');
DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');
DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');
L'operatore Split aggiunge una colonna di codice di azione allo stream (qui etichettato Act1007):
Il codice azione è 1 per un aggiornamento, 3 per un'eliminazione e 4 per un inserimento.
Ordinare
Le suddette istruzioni divise genererebbero comunque una violazione della chiave univoca transitoria falsa, quindi il passaggio successivo consiste nell'ordinare le istruzioni in base alle chiavi dell'indice univoco in fase di aggiornamento ( pk
in questo caso), quindi in base al codice di azione. Per questo esempio, ciò significa semplicemente che le eliminazioni (3) sulla stessa chiave sono ordinate prima degli inserti (4). L'ordine risultante è:
-- Sort (pk, action)
DELETE dbo.Banana WHERE pk = 1;
DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');
DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');
DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');
Crollo
La fase precedente è sufficiente per garantire la prevenzione di violazioni della falsa unicità in tutti i casi. Come ottimizzazione, Collapse combina eliminazioni e inserimenti adiacenti sullo stesso valore chiave in un aggiornamento:
-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');
Le coppie delete / insert per i pk
valori 2, 3 e 4 sono state combinate in un aggiornamento, lasciando una singola eliminazione su pk
= 1 e un'inserzione per pk
= 5.
L'operatore Comprimi raggruppa le righe in base alle colonne chiave e aggiorna il codice di azione per riflettere il risultato della compressione:
Aggiornamento dell'indice cluster
Questo operatore è etichettato come un aggiornamento, ma è in grado di inserire, aggiornare ed eliminare. L'azione eseguita dall'aggiornamento dell'indice cluster per riga è determinata dal valore del codice azione in quella riga. L'operatore ha una proprietà Action per riflettere questa modalità operativa:
Contatori di modifica delle righe
Si noti che i tre aggiornamenti precedenti non modificano le chiavi dell'indice univoco da mantenere. In effetti, abbiamo trasformato gli aggiornamenti delle colonne chiave nell'indice in aggiornamenti delle colonne non chiave ( c1
e c2
), oltre a una cancellazione e un inserimento. Né una cancellazione né un inserimento possono causare una falsa violazione della chiave univoca.
Un inserimento o un'eliminazione influisce su ogni singola colonna della riga, quindi le statistiche associate a ogni colonna avranno i contatori delle modifiche incrementati. Per gli aggiornamenti, solo le statistiche con una delle colonne aggiornate come colonna principale hanno i loro contatori di modifiche incrementati (anche se il valore è invariato).
I contatori di modifica delle righe delle statistiche mostrano quindi 2 modifiche pk
e 5 per c1
e c2
:
-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1; -- All columns modified
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2; -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3; -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4; -- c1 and c2 modified
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z'); -- All columns modified
Nota: solo le modifiche applicate all'oggetto base (heap o indice cluster) influiscono sui contatori di modifica delle righe delle statistiche. Gli indici non cluster sono strutture secondarie, che riflettono le modifiche già apportate all'oggetto base. Non influiscono affatto sui contatori di modifica delle righe delle statistiche.
Se un oggetto ha più indici univoci, viene utilizzata una combinazione separata di suddivisione, ordinamento e compressione per organizzare gli aggiornamenti per ciascuno. SQL Server ottimizza questo caso per gli indici non cluster salvando il risultato della divisione in uno spool della tabella Eager, quindi riproducendo tale set per ciascun indice univoco (che avrà il proprio ordinamento per chiavi di indice + codice azione e Comprimi).
Effetto sugli aggiornamenti delle statistiche
Gli aggiornamenti automatici delle statistiche (se abilitati) si verificano quando Query Optimizer necessita di informazioni statistiche e rileva che le statistiche esistenti non sono aggiornate (o non sono valide a causa di una modifica dello schema). Le statistiche sono considerate obsolete quando il numero di modifiche registrate supera una soglia.
La disposizione Split / Sort / Collapse comporta la registrazione di diverse modifiche di riga di quanto ci si aspetterebbe. Questo, a sua volta, significa che un aggiornamento delle statistiche potrebbe essere attivato prima o poi di quanto non sarebbe altrimenti.
Nell'esempio sopra, le modifiche alla riga per la colonna chiave aumentano di 2 (la variazione netta) invece di 4 (una per ogni riga della tabella interessata) o 5 (una per ogni eliminazione / aggiornamento / inserimento prodotta dal Collapse).
Inoltre, le colonne non chiave che non sono state logicamente modificate dalla query originale accumulano modifiche alle righe, che possono numerare fino al doppio delle righe della tabella aggiornate (una per ogni eliminazione e una per ogni inserimento).
Il numero di modifiche registrate dipende dal grado di sovrapposizione tra i valori della colonna chiave vecchia e nuova (e quindi il grado in cui è possibile comprimere le singole eliminazioni e inserimenti). Reimpostando la tabella tra ogni esecuzione, le seguenti query dimostrano l'effetto sui contatori di modifica delle righe con diverse sovrapposizioni:
UPDATE dbo.Banana SET pk = pk + 0; -- Full overlap
UPDATE dbo.Banana SET pk = pk + 1;
UPDATE dbo.Banana SET pk = pk + 2;
UPDATE dbo.Banana SET pk = pk + 3;
UPDATE dbo.Banana SET pk = pk + 4; -- No overlap