Ändern Sie die Spalte NULL schnell in NOT NULL

11

Ich habe eine Tabelle mit Millionen von Zeilen und eine Spalte, die Nullwerte zulässt. Derzeit hat jedoch keine Zeile einen NULL-Wert für diese Spalte (dies kann ich relativ schnell mit einer Abfrage überprüfen). Jedoch wenn ich den Befehl ausführe

ALTER TABLE MyTable ALTER COLUMN MyColumn BIGINT NOT NULL;

Die Abfrage dauert relativ gesehen ewig . Tatsächlich dauert es zwischen 10 und 20 Minuten, mehr als doppelt so lange wie das Hinzufügen einer Prüfbedingung. Gibt es eine Möglichkeit, die Metadaten der Tabelle für diese Spalte sofort zu aktualisieren, zumal ich weiß, dass keine Zeile einen NULL-Wert für diese Spalte hat?

Joseph Daigle
quelle
2
Nein (oder zumindest nicht mit dokumentierten / unterstützten Methoden). Siehe Warum verursacht ALTER COLUMN bis NOT NULL ein massives Wachstum der Protokolldateien?
Martin Smith
2
Möglicherweise wartet es auch auf eine Sch-MSperre, wenn es "für immer" dauert. Haben Sie nachgesehen, ob es gewartet hat oder beschäftigt ist?
Martin Smith
@MartinSmith Ich habe klargestellt, was ich unter für immer verstehe . Ich teste dies in einer Entwicklungsumgebung, in der keine anderen Sitzungen auf die Datenbank treffen. Es wird schließlich abgeschlossen. Aber die Antwort, die Sie verlinkt haben, erklärt, warum es so lange dauert. Wenn Sie Ihren Kommentar als Antwort umformulieren können, werde ich ihn akzeptieren.
Joseph Daigle

Antworten:

12

Die Antwort von @ypercube verwaltet dies teilweise, da sich nur die Metadaten ändern.

Das Hinzufügen der Einschränkung mit NOCHECKbedeutet, dass keine Zeilen gelesen werden müssen, um sie zu überprüfen. Wenn Sie an einer Position beginnen, an der die Spalte keine NULLWerte enthält (und wenn Sie wissen, dass zwischen dem Überprüfen und Hinzufügen der Einschränkung keine hinzugefügt werden), Da die Einschränkung verhindert NULL, dass Werte aus zukünftigen INSERToder UPDATEVorgängen erstellt werden, funktioniert dies.

Das Hinzufügen der Einschränkung kann sich jedoch weiterhin auf gleichzeitige Transaktionen auswirken. Sie ALTER TABLEmüssen Sch-Mzuerst eine Sperre erwerben . Während des Wartens werden alle anderen Tabellenzugriffe wie hier beschrieben gesperrt .

Sobald die Sch-MSperre erreicht ist, sollte die Operation jedoch ziemlich schnell sein.

Ein Problem dabei ist, dass selbst wenn Sie wissen, dass die Spalte tatsächlich keine NULLs enthält, der Einschränkung vom Abfrageoptimierer nicht vertraut wird, was bedeutet, dass die Pläne suboptimal sein können.

CREATE TABLE T (X INT NULL)

INSERT INTO T 
SELECT ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values

ALTER TABLE T WITH NOCHECK
  ADD  CONSTRAINT X_NOT_NULL 
    CHECK (X IS NOT NULL) ; 

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Planen

Vergleichen Sie dies mit dem Einfacheren

ALTER TABLE T ALTER COLUMN X INT NOT NULL

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Planen

Ein mögliches Problem, das beim Ändern der Spaltendefinition auf diese Weise auftreten kann, besteht darin, dass nicht nur alle Zeilen gelesen werden müssen, um zu überprüfen, ob sie die Bedingung erfüllen, sondern auch protokollierte Aktualisierungen für die Zeilen ausgeführt werden können .

Ein möglicher Zwischenstopp könnte darin bestehen, die Prüfbedingung hinzuzufügen WITH CHECK. Dies ist langsamer als WITH NOCHECKdas Lesen aller Zeilen, aber es ermöglicht dem Abfrageoptimierer, den einfacheren Plan in der obigen Abfrage anzugeben, und es sollte das mögliche Problem mit protokollierten Aktualisierungen vermeiden.

Martin Smith
quelle
7

Anstatt die Spalte zu ändern, können Sie eine Tabelleneinschränkung CHECKmit der folgenden NOCHECKOption hinzufügen :

ALTER TABLE MyTable WITH NOCHECK
  ADD  CONSTRAINT MyColumn_NOT_NULL 
    CHECK (MyColumn IS NOT NULL) ;
ypercubeᵀᴹ
quelle
1
Dies würde zukünftige Aktualisierungen oder Einfügungen verhindern, die die Spalte NULLerzeugen, aber nicht vom Abfrageoptimierer verwendet werden können.
Martin Smith
@MartinSmith Oh ja, ich habe gerade die Antwort und die Kommentare in der ähnlichen Frage gelesen: Wie füge ich einer großen Tabelle in SQL Server eine NOT NULL-Spalte hinzu? Bitte fügen Sie eine Antwort mit den Problemen oder eine bessere Lösung hinzu und ich werde meine entfernen.
Ypercubeᵀᴹ
2
Ich habe keine bessere Lösung. Ich habe dem zugestimmt, weil es eine Teillösung bietet. Wenn das OP nur ungültige Daten verhindern möchte, funktioniert es (und sollte schneller sein, als ALTER COLUMNwenn die Sch-MSperre einmal erlangt wurde, müssen die Zeilen überhaupt nicht gescannt werden). Ich möchte nur darauf hinweisen, dass es nicht ganz dasselbe ist (z. B. wenn NOT INder Plan in einer Abfrage verwendet wird, wird er komplexer)
Martin Smith