IF-Anweisung überspringt TempDB nicht, wenn Datenbanken mit sp_MSForEachDB durchlaufen werden

8

[SQL Server 2012 SP2 EE]

Warum gibt mir das folgende Skript einen Fehler in Bezug auf Tempdb?

    exec sp_MSForEachDB '
    IF ( (select database_id from sys.databases where name = ''?'') > 4)
    BEGIN 
    ALTER AUTHORIZATION ON DATABASE::? TO [sa];
    ALTER DATABASE [?] SET RECOVERY SIMPLE;
    END'

Hier ist der Fehler, den ich bekomme:

  Msg 5058, Level 16, State 1, Line 5
  Option 'RECOVERY' cannot be set in database 'tempdb'.

Es macht den Job, den es machen soll. Aber ich kann mir keinen Grund für den Fehler vorstellen. Ich weiß, dass die Datenbank-ID der Tempdb 2 ist, dann sollte sie zumindest nicht versuchen, die Option für Tempdb festzulegen.

GaganLamba
quelle

Antworten:

4

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 ALTERAnweisungen 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 IFAnweisung 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 EXECAufruf 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_MSForEachDBdie Datenbanken durchlaufen und Ihr übergebenes SQL ausführen, nachdem Sie das ?durch den aktuellen Datenbanknamen ersetzt haben. Wenn der Cursor innerhalb von erreicht sp_MSForEachDBwird, [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:

  1. Analysieren
  2. Kompilieren
  3. 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...ENDBlocks von je ausgeführt wird , doch der Fehler immer noch auftritt. Daher tritt der Fehler nicht auf, weil tatsächlich versucht wurde, die beiden ALTERAnweisungen auszuführen .

Der Grund , dass die Verpackung ALTERAnweisungen 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 IFAnweisung wird überspringen , [tempdb]und die Fehler „Option‚Verwertung‘kann nicht in der Datenbank‚tempdb‘gesetzt werden“ wird nicht passieren.

Solomon Rutzky
quelle
Meine Frage ist - Warum ist die "if" -Anweisung, mit der die ID von tempdb (dh 2) an den if-Anweisungsblock gesendet werden kann, wenn sie nicht die Bedingung erfüllt, größer als 4 zu sein
?
1
@GaganLamba Ich verstehe deine Frage und habe sie sehr spezifisch beantwortet. Hast du meine Beispiele angeführt? Wie ich in der Antwort angegeben habe, handelt es sich um einen Fehler zur Kompilierungszeit und nicht zur Laufzeit. Daher werden zu diesem Zeitpunkt weder die IFAnweisung noch andere SQL-Anweisungen (einschließlich der beiden ALTERs) ausgeführt. Die IFAnweisung erlaubt nicht, dass die ID von tempdb an das gesendet wird, was sich im BEGIN/ ENDblock befindet, und der Code führt die ALTERAnweisungen zu diesem Zeitpunkt noch nicht einmal aus. Der Fehler wird von SQL Server ausgelöst, da SQL vor der Ausführung überprüft wird. Ich habe am Ende einen Zusammenfassungsabschnitt hinzugefügt.
Solomon Rutzky
3

Meldung 5058, Ebene 16, Status 1, Zeile 5 Die Option 'WIEDERHERSTELLUNG' kann in der Datenbank 'tempdb' nicht festgelegt werden.

Es macht den Job, den es machen soll. Aber ich kann mir keinen Grund für den Fehler vorstellen.

Erstens ist es nicht erforderlich, ms_foreachdbdas undokumentierte zu verwenden, und es ist sehr schlecht, dass Sie mit einem einfachen Cursor eine Schleife erstellen können. In Bezug auf Fehler versuchen Sie, das Wiederherstellungsmodell aller Datenbanken zu ändern, including tempdbaber Sie können weder das Wiederherstellungsmodell von tempdb ändern, noch können Sie einen Sicherungsvorgang ausführen, weshalb Sie diese Fehlermeldung erhalten haben. Dies ist von Microsoft nicht zulässig. Bitte lesen Sie mehr über Operationen, die Sie mit Tempdb ausführen können

Shanky
quelle
1
Ich verstehe, dass ms_foreachdb nicht dokumentiert ist und ich es nicht verwenden sollte. Ich verstehe auch, dass Tempdb nicht gesichert werden soll oder die Wiederherstellungsoption ändern soll. Meine Frage ist jedoch immer noch unbeantwortet, warum SQL Server versucht, die Option für TEMPDB zu ändern. Die TEMPDB-ID (2) wurde nicht einmal zurückgegeben.
GaganLamba
1
@GaganLamba SQL Server versucht nicht, die Option für TEMPDB zu ändern. Die ALTERAnweisungen werden lediglich validiert und nicht ausgeführt. Ich gebe Details in meiner Antwort an .
Solomon Rutzky
3

Abgesehen von der Tatsache, dass Sie die Wiederherstellungsoption für Tempdb nicht ändern können, benötigen Sie keine Schleife für das, was Sie tun:

Führen Sie SSMS durch Drücken von CTRL+ ausT

select 'alter authorization on database::' + quotename(name) + ' to [sa];' + char(10) + 'alter database ' + quotename(name) + ' set recovery simple;'
from sys.databases
where database_id > 4
    and state_desc = 'ONLINE'
Kin Shah
quelle