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 pk
von 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
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):
Der Aktionscode ist 1 für eine Aktualisierung, 3 für eine Löschung und 4 für eine Einfügung.
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 ( pk
in 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');
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 pk
Werte 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:
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:
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 ( c1
und 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 pk
und 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
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