Ich habe mehrere Quellen gefunden, die ALTER TABLE angeben ... DROP COLUMN ist eine reine Metadatenoperation.
Wie kann das sein? Müssen die Daten während einer DROP COLUMN nicht aus den zugrunde liegenden nicht gruppierten Indizes und dem gruppierten Index / Heap gelöscht werden?
Warum implizieren die Microsoft-Dokumente außerdem , dass es sich um einen vollständig protokollierten Vorgang handelt?
Die an der Tabelle vorgenommenen Änderungen werden protokolliert und können vollständig wiederhergestellt werden. Änderungen, die sich auf alle Zeilen in großen Tabellen auswirken, z. B. das Löschen einer Spalte oder in einigen Editionen von SQL Server das Hinzufügen einer NOT NULL-Spalte mit einem Standardwert, können lange dauern, bis viele Protokolldatensätze abgeschlossen und generiert sind . Führen Sie diese ALTER TABLE-Anweisungen mit der gleichen Sorgfalt aus wie alle INSERT-, UPDATE- oder DELETE-Anweisungen, die viele Zeilen betreffen.
Als sekundäre Frage: Wie verfolgt die Engine abgelegte Spalten, wenn die Daten nicht von den zugrunde liegenden Seiten entfernt werden?
quelle
Antworten:
Unter bestimmten Umständen kann das Löschen einer Spalte eine reine Metadatenoperation sein. Die Spaltendefinitionen für eine bestimmte Tabelle sind nicht auf jeder Seite enthalten, auf der Zeilen gespeichert werden. Spaltendefinitionen werden nur in den Datenbankmetadaten gespeichert, einschließlich sys.sysrowsets, sys.sysrscols usw.
Wenn Sie eine Spalte löschen, auf die von keinem anderen Objekt verwiesen wird, markiert die Speicher-Engine die Spaltendefinition einfach als nicht mehr vorhanden, indem sie die relevanten Details aus verschiedenen Systemtabellen löscht. Das Löschen der Metadaten macht den Prozedurcache ungültig und erfordert eine Neukompilierung, wenn eine Abfrage anschließend auf diese Tabelle verweist. Da die Neukompilierung nur Spalten zurückgibt, die derzeit in der Tabelle vorhanden sind, werden die Spaltendetails für die abgelegte Spalte nicht einmal abgefragt. Die Speicher-Engine überspringt die auf jeder Seite für diese Spalte gespeicherten Bytes, als ob die Spalte nicht mehr vorhanden wäre.
Wenn eine nachfolgende DML-Operation für die Tabelle ausgeführt wird, werden die betroffenen Seiten ohne die Daten für die abgelegte Spalte neu geschrieben. Wenn Sie einen Clustered-Index oder einen Heap neu erstellen, werden natürlich nicht alle Bytes für die abgelegte Spalte auf die Seite auf der Festplatte zurückgeschrieben. Dadurch wird die Last des Fallens der Säule im Laufe der Zeit effektiv verteilt, wodurch sie weniger auffällt.
Unter bestimmten Umständen können Sie eine Spalte nicht löschen, z. B. wenn die Spalte in einem Index enthalten ist oder wenn Sie manuell ein Statistikobjekt für die Spalte erstellt haben. Ich habe einen Blog-Beitrag geschrieben , der den Fehler zeigt, der beim Versuch auftritt, eine Spalte mit einem manuell erstellten Statistikobjekt zu ändern. Die gleiche Semantik gilt beim Löschen einer Spalte. Wenn die Spalte von einem anderen Objekt referenziert wird , kann sie nicht einfach gelöscht werden. Das referenzierende Objekt muss zuerst geändert werden, dann kann die Spalte gelöscht werden.
Dies lässt sich relativ einfach anzeigen, indem Sie den Inhalt des Transaktionsprotokolls nach dem Löschen einer Spalte anzeigen. Der folgende Code erstellt eine Tabelle mit einer einzelnen 8.000 langen Zeichenspalte. Es fügt eine Zeile hinzu, löscht sie dann und zeigt den Inhalt des Transaktionsprotokolls an, der für die Löschoperation gilt. Die Protokollsätze zeigen Änderungen an verschiedenen Systemtabellen, in denen die Tabellen- und Spaltendefinitionen gespeichert sind. Wenn die Spaltendaten tatsächlich von den der Tabelle zugewiesenen Seiten gelöscht wurden, werden Protokolldatensätze angezeigt, in denen die tatsächlichen Seitendaten aufgezeichnet werden. Es gibt keine solchen Aufzeichnungen.
(Die Ausgabe ist zu groß, um hier angezeigt zu werden, und dbfiddle.uk erlaubt mir nicht, auf fn_dblog zuzugreifen.)
Der erste Ausgabesatz zeigt das Protokoll als Ergebnis der DDL-Anweisung, die die Spalte löscht. Der zweite Ausgabesatz zeigt das Protokoll nach dem Ausführen der DML-Anweisung, in der die
rid
Spalte aktualisiert wird. In der zweiten Ergebnismenge sehen wir Protokolldatensätze, die ein Löschen für dbo.DropColumnTest anzeigen, gefolgt von einer Einfügung in dbo.DropColumnTest. Jede Protokolldatensatzlänge beträgt 8116, was angibt, dass die tatsächliche Seite aktualisiert wurde.Wie Sie aus dem Ausgang des sehen
fn_dblog
Befehls im Test oben, der gesamte Vorgang wird vollständig protokolliert. Dies gilt sowohl für die einfache als auch für die vollständige Wiederherstellung. Die Terminologie "vollständig protokolliert" wird möglicherweise falsch interpretiert, da die Datenänderung nicht protokolliert wird. Dies ist nicht der Fall - die Änderung wird protokolliert und kann vollständig zurückgesetzt werden. Das Protokoll zeichnet einfach nur die Seiten auf, die berührt wurden, und da keine der Datenseiten der Tabelle durch die DDL-Operation protokolliert wurde, werden sowohl dasDROP COLUMN
als auch eventuell auftretende Rollback unabhängig von der Größe der Tabelle extrem schnell durchgeführt.Für die Wissenschaft werden mit dem folgenden Code die Datenseiten für die im obigen Code enthaltene Tabelle im
DBCC PAGE
Stil "3" ausgegeben. Der Stil "3" gibt an, dass wir den Seitenkopf sowie eine detaillierte Interpretation pro Zeile wünschen . Der Code verwendet einen Cursor, um die Details für jede Seite in der Tabelle anzuzeigen. Sie sollten daher sicherstellen, dass Sie dies nicht für eine große Tabelle ausführen.Wenn ich mir die Ausgabe für die erste Seite meiner Demo ansehe (nachdem die Spalte gelöscht wurde, aber bevor die Spalte aktualisiert wurde), sehe ich Folgendes:
Der Kürze halber habe ich den größten Teil des Rohseiten-Dumps aus der oben gezeigten Ausgabe entfernt. Am Ende der Ausgabe sehen Sie dies für die
rid
Spalte:Die letzte Zeile oben
rid = 1
gibt den Namen der Spalte und den aktuellen Wert zurück, der in der Spalte auf der Seite gespeichert ist.Als nächstes sehen Sie Folgendes:
Die Ausgabe zeigt, dass Slot 0 aufgrund des
DELETED
Textes, in dem sich normalerweise der Spaltenname befindet, eine gelöschte Spalte enthält . Der Wert der Spalte wird zurückgegeben,NULL
seit die Spalte gelöscht wurde. Wie Sie jedoch in den Rohdaten sehen können, ist der Wert von 8.000 ZeichenREPLICATE('Z', 8000)
für diese Spalte auf der Seite noch vorhanden. Dies ist ein Beispiel für diesen Teil der DBCC-PAGE-Ausgabe:quelle