Aggiornamenti univoci dell'indice e contatori di modifica delle righe delle statistiche


14

Data la tabella seguente, l'indice cluster univoco e le statistiche:

CREATE TABLE dbo.Banana
(
    pk integer NOT NULL, 
    c1 char(1) NOT NULL, 
    c2 char(1) NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX pk ON dbo.Banana (pk);

CREATE STATISTICS c1 ON dbo.Banana (c1);
CREATE STATISTICS c2 ON dbo.Banana (c2);

INSERT dbo.Banana 
    (pk, c1, c2) 
VALUES 
    (1, 'A', 'W'), 
    (2, 'B', 'X'), 
    (3, 'C', 'Y'), 
    (4, 'D', 'Z');

-- Populate statistics
UPDATE STATISTICS dbo.Banana;

Dati di esempio

I contatori di modifica delle righe delle statistiche mostrano ovviamente zero prima di qualsiasi aggiornamento:

-- Show statistics modification counters
SELECT
    stats_name = S.[name], 
    DDSP.stats_id,
    DDSP.[rows],
    DDSP.modification_counter
FROM sys.stats AS S
CROSS APPLY sys.dm_db_stats_properties(S.object_id, S.stats_id) AS DDSP
WHERE
    S.[object_id] = OBJECT_ID(N'dbo.Banana', N'U');

Contatori di modifiche zero

Incrementare il pkvalore di ogni colonna di uno per ogni riga:

-- Increment pk in every row
UPDATE dbo.Banana 
SET pk += 1;

Utilizza il piano di esecuzione:

Suddividi ordinamento Comprimi piano di esecuzione

Produce i seguenti contatori di modifica delle statistiche:

Contatori delle modifiche post-aggiornamento

Domande

  1. Cosa fanno gli operatori Dividi, Ordina e Comprimi?
  2. Perché le pkstatistiche mostrano 2 modifiche, ma c1e c2mostrano 5?

Risposte:


15

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 pkda 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 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):

Proprietà divise

Il codice azione è 1 per un aggiornamento, 3 per un'eliminazione e 4 per un inserimento.

Ordinare 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 ( pkin 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');

Ordina proprietà

Crollo 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 pkvalori 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:

Comprimi proprietà

Aggiornamento dell'indice cluster 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:

Proprietà dell'azione Aggiornamento indice cluster


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 ( c1e 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 pke 5 per c1e 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

pk = pk + 0

UPDATE dbo.Banana SET pk = pk + 1;

pk = pk + 1

UPDATE dbo.Banana SET pk = pk + 2;

pk = pk + 2

UPDATE dbo.Banana SET pk = pk + 3;

pk = pk + 3

UPDATE dbo.Banana SET pk = pk + 4; -- No overlap

pk = pk + 4

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.