Einzigartige Indexaktualisierungen und Änderungszähler für Statistikzeilen

14

Ausgehend von der folgenden Tabelle, dem eindeutigen Clustered-Index und den Statistiken:

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;

Beispieldaten

Die Statistikzeilenänderungszähler zeigen offensichtlich Null an, bevor Aktualisierungen vorgenommen werden:

-- 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');

Änderungsfreie Zähler

Inkrementieren Sie jeden pkSpaltenwert für jede Zeile um eins:

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

Verwendet den Ausführungsplan:

Ausführungsplan für Split Sort Collapse

Es werden die folgenden Statistikänderungszähler erstellt:

Änderungszähler nach dem Update

Fragen

  1. Was machen die Operatoren Teilen, Sortieren und Reduzieren?
  2. Warum zeigen die pkStatistiken 2 Modifikationen, aber c1und c25?
Paul White Monica wieder einsetzen
quelle

Antworten:

15

SQL Server verwendet immer die Operatorkombination Teilen , Sortieren und Reduzieren, wenn ein eindeutiger Index als Teil einer Aktualisierung verwaltet wird, die mehr als eine Zeile betrifft (oder betreffen könnte).

Wenn wir uns das Beispiel in der Frage ansehen, könnten wir das Update als separates einzeiliges Update für jede der vier vorhandenen Zeilen schreiben:

-- 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;

Das Problem besteht darin, dass die erste Anweisung fehlschlagen würde, da sie pkvon 1 in 2 geändert wird und bereits eine Zeile mit pk= 2 vorhanden ist. Das SQL Server-Speichermodul erfordert, dass eindeutige Indizes in jeder Verarbeitungsstufe eindeutig bleiben, auch innerhalb einer einzelnen Anweisung . Dies ist das Problem, das durch Teilen, Sortieren und Reduzieren gelöst wurde.

Teilt Teilt

Der erste Schritt besteht darin, jede Aktualisierungsanweisung in einen Löschvorgang aufzuteilen, gefolgt von einem Einfügungsvorgang:

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');

Der Split-Operator fügt dem Stream eine Aktionscode-Spalte hinzu (hier Act1007):

Eigenschaften aufteilen

Der Aktionscode ist 1 für eine Aktualisierung, 3 für eine Löschung und 4 für eine Einfügung.

Sortieren Sortieren

Die oben genannten geteilten Anweisungen würden immer noch eine vorübergehende Verletzung des eindeutigen Schlüssels verursachen. Der nächste Schritt besteht darin, die Anweisungen nach den Schlüsseln des zu aktualisierenden eindeutigen Index zu sortieren ( pkin diesem Fall) und dann nach dem Aktionscode. In diesem Beispiel bedeutet dies einfach, dass Löschungen (3) auf demselben Schlüssel vor Einfügungen (4) angeordnet werden. Die resultierende Reihenfolge ist:

-- 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');

Eigenschaften sortieren

Zusammenbruch Zusammenbruch

Die vorhergehende Stufe reicht aus, um in allen Fällen die Vermeidung falscher Eindeutigkeitsverletzungen zu gewährleisten. Als Optimierung kombiniert Collapse benachbarte Löschvorgänge und Einfügungen für denselben Schlüsselwert in einer Aktualisierung:

-- 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');

Die Lösch- / Einfügepaare für die pkWerte 2, 3 und 4 wurden zu einer Aktualisierung zusammengefasst, wobei ein einzelnes Löschen auf pk= 1 und ein Einfügen auf pk= 5 belassen wurde .

Der Collapse-Operator gruppiert Zeilen nach den Schlüsselspalten und aktualisiert den Aktionscode, um das Collapse-Ergebnis widerzuspiegeln:

Eigenschaften reduzieren

Clustered-Index-Aktualisierung Clustered-Index-Aktualisierung

Dieser Operator ist als Update gekennzeichnet, kann jedoch Einfügungen, Aktualisierungen und Löschvorgänge ausführen. Welche Aktion von der Clustered Index-Aktualisierung pro Zeile ausgeführt wird, hängt vom Wert des Aktionscodes in dieser Zeile ab. Der Operator verfügt über eine Action-Eigenschaft, die diesen Betriebsmodus widerspiegelt:

Aktionseigenschaft Clustered Index Update


Zeilenänderungszähler

Beachten Sie, dass die drei oben genannten Aktualisierungen nicht die Schlüssel des eindeutigen Index ändern, der verwaltet wird. Im Endeffekt haben wir Aktualisierungen für das Schlüsselspalten im Index in Aktualisierungen der Nichtschlüsselspalten ( c1und c2) sowie ein Löschen und Einfügen umgewandelt. Weder ein Löschen noch ein Einfügen kann eine falsche Verletzung des eindeutigen Schlüssels verursachen.

Einfügen oder Löschen wirkt sich auf jede einzelne Spalte in der Zeile aus, sodass die Änderungszähler für Statistiken, die mit jeder Spalte verknüpft sind, erhöht werden. Bei Aktualisierungen werden nur für Statistiken mit einer der aktualisierten Spalten als führende Spalte die Änderungszähler erhöht (auch wenn der Wert unverändert bleibt).

Die Statistikzeilen-Änderungszähler zeigen daher 2 Änderungen an pkund 5 fürc1 und 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

Hinweis: Nur Änderungen, die auf das Basisobjekt (Heap oder Clustered Index) angewendet werden, wirken sich auf die Änderungszähler für Statistikzeilen aus. Nicht gruppierte Indizes sind Sekundärstrukturen, die Änderungen widerspiegeln, die bereits am Basisobjekt vorgenommen wurden. Sie wirken sich überhaupt nicht auf die Änderungszähler für Statistikzeilen aus.

Wenn ein Objekt mehrere eindeutige Indizes hat, wird eine separate Kombination aus Teilen, Sortieren und Reduzieren verwendet, um die Aktualisierungen für jeden zu organisieren. SQL Server optimiert diesen Fall für nicht gruppierte Indizes, indem das Ergebnis der Aufteilung in einem Eager-Tabellenspool gespeichert und dann dieser Satz für jeden eindeutigen Index (der über einen eigenen Indexschlüssel + Aktionscode zum Sortieren und Reduzieren verfügt) wiedergegeben wird.

Auswirkung auf Statistikaktualisierungen

Automatische Statistikaktualisierungen (falls aktiviert) erfolgen, wenn das Abfrageoptimierungsprogramm statistische Informationen benötigt und feststellt, dass vorhandene Statistiken nicht mehr aktuell sind (oder aufgrund einer Schemaänderung ungültig sind). Statistiken gelten als veraltet, wenn die Anzahl der erfassten Änderungen einen Schwellenwert überschreitet.

Die Split / Sort / Collapse-Anordnung führt dazu, dass andere Zeilenmodifikationen aufgezeichnet werden als erwartet. Dies bedeutet wiederum, dass eine Statistikaktualisierung früher oder später ausgelöst werden kann, als dies sonst der Fall wäre.

Im obigen Beispiel erhöhen sich die Zeilenänderungen für die Schlüsselspalte um 2 (die Nettoveränderung) anstatt um 4 (eine für jede betroffene Tabellenzeile) oder um 5 (eine für jedes Löschen / Aktualisieren / Einfügen, das durch das Reduzieren erzeugt wurde).

Darüber hinaus werden bei Nichtschlüsselspalten, die durch die ursprüngliche Abfrage logisch nicht geändert wurden, Zeilenänderungen vorgenommen, die doppelt so viele Tabellenzeilen enthalten können wie aktualisiert wurden (eine für jedes Löschen und eine für jedes Einfügen).


Die Anzahl der aufgezeichneten Änderungen hängt vom Grad der Überlappung zwischen alten und neuen Schlüsselspaltenwerten ab (und damit vom Grad, bis zu dem die einzelnen Lösch- und Einfügevorgänge reduziert werden können). Das Zurücksetzen der Tabelle zwischen den einzelnen Ausführungen zeigt die Auswirkung der folgenden Abfragen auf Zeilenänderungszähler mit unterschiedlichen Überlappungen:

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

Paul White Monica wieder einsetzen
quelle