HINWEIS FÜR DIE LESER: Bitte lesen Sie die gesamte Frage einschließlich des Beispielcodes (dh nicht nur den Titel). Bei dieser Frage geht es weder darum, wie Datenbanken am besten durchlaufen werden, noch darum, warum [tempdb] diesen Fehler erhält. Das OP versucht bereits, die Ausführung der ALTER
Anweisungen in allen Systemdatenbanken (also den 4 sichtbaren) zu vermeiden, und fragt, warum die IF-Anweisung, die [tempdb] überspringen soll, sie nicht zu überspringen scheint.
Warum gibt mir das folgende Skript einen Fehler in Bezug auf Tempdb?
Der Grund dafür ist, dass die IF
Anweisung nur Auswirkungen darauf hat, was passiert, wenn der Code tatsächlich ausgeführt wird. SQL Server muss den Stapel jedoch noch analysieren und kompilieren, bevor er ausgeführt wird. Analysefehler beziehen sich auf die Syntax, z. B. um sicherzustellen, dass SQL-Anweisungen ordnungsgemäß erstellt und Variablen ordnungsgemäß deklariert wurden:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
SELECT @Bob;
erhält folgenden Fehler:
Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "@Bob".
Und die folgende:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
CREATE TABLE b
erhält folgenden Fehler:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'b'.
Wenn der Stapel erfolgreich analysiert wird, wird er kompiliert. Zu diesem Zeitpunkt werden beispielsweise Berechtigungen überprüft und einige andere Überprüfungen durchgeführt.
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
IF (1 = 0)
BEGIN
ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
END;
Das obige SQL ist ordnungsgemäß erstellt, sodass der Stapel erfolgreich analysiert werden kann. Versuchen Sie nun, das obige SQL auszuführen, indem Sie F5 oder Control-E oder das ! Schaltfläche " Ausführen" usw.
Diesmal wird folgende Fehlermeldung angezeigt:
Msg 5058, Level 16, State 1, Line 4
Option 'RECOVERY' cannot be set in database 'tempdb'.
obwohl das IF (1 = 0)
sicherstellt, dass der Code nie ausgeführt wird. Dies bedeutet, dass Sie auf einen Kompilierungsfehler stoßen. Sie können diese Art von Fehlern umgehen, indem Sie den fehlerhaften Code über einen EXEC
Aufruf in einen Unterprozess verschieben .
Führen Sie Folgendes aus, und es wird erfolgreich abgeschlossen, da das, was sich in dem EXEC()
befindet, erst analysiert oder kompiliert wird, wenn diese Anweisung zur Laufzeit ausgeführt wird.
IF (1 = 0)
BEGIN
EXEC('
ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
');
END;
Zusammenfassend:
Betrachten Sie zunächst, dass Sie sp_MSForEachDB
die Datenbanken durchlaufen und Ihr übergebenes SQL ausführen, nachdem Sie das ?
durch den aktuellen Datenbanknamen ersetzt haben. Wenn der Cursor innerhalb von erreicht sp_MSForEachDB
wird, [tempdb]
führt er effektiv Folgendes aus:
IF ( (select database_id from sys.databases where name = ''tempdb'') > 4)
BEGIN
ALTER AUTHORIZATION ON DATABASE::tempdb TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
END
Zweitens führt SQL Server mehrere Schritte aus, wenn Sie einen Abfragebatch ausführen:
- Analysieren
- Kompilieren
- Tatsächliche Ausführung
Es ist wichtig zu verstehen, dass dies separate Schritte sind und Fehler erzeugen können, bevor Sie mit dem nächsten Schritt fortfahren (und daher die weitere Verarbeitung abbrechen, bevor Sie mit dem nächsten Schritt fortfahren). In diesem Fall tritt der Fehler in Schritt 2 - Kompilieren - auf, wie im vorletzten Beispiel oben gezeigt (das erste, mit dem begonnen wird IF (1 = 0)
). Die IF (1 = 0)
verhindert , dass der Code innerhalb des BEGIN...END
Blocks von je ausgeführt wird , doch der Fehler immer noch auftritt. Daher tritt der Fehler nicht auf, weil tatsächlich versucht wurde, die beiden ALTER
Anweisungen auszuführen .
Der Grund , dass die Verpackung ALTER
Anweisungen innerhalb der EXEC()
Funktion funktioniert, weil SQL Server nicht analysieren und kompilieren , was in der ist , EXEC()
bis die EXEC()
tatsächlich ausgeführt wird . An diesem Punkt das IF ( (select database_id from sys.databases where name = ''?'') > 4)
wird Anweisung nicht zulässig , da die Charge während Compilation fehlschlagen werden , nicht laufen und werden es bis zur Ausführung machen, und die IF
Anweisung wird überspringen , [tempdb]
und die Fehler „Option‚Verwertung‘kann nicht in der Datenbank‚tempdb‘gesetzt werden“ wird nicht passieren.