NOTA PER I LETTORI: si prega di leggere l'intera domanda, incluso il codice di esempio (cioè non solo il titolo). Questa domanda non riguarda il modo migliore per scorrere i database, né il motivo per cui [tempdb] riceve questo errore. L'OP sta già cercando di evitare di eseguire le ALTER
istruzioni su tutti i database di sistema (bene, i 4 visibili) e chiede perché l'istruzione IF che dovrebbe saltare [tempdb] non sembra saltarla.
Perché il seguente script mi dà un errore relativo a tempdb?
Il motivo è che l' IF
istruzione influisce solo su ciò che accade quando il codice è effettivamente in esecuzione, ma SQL Server deve ancora analizzare e compilare il batch prima di eseguirlo. Gli errori di analisi sono quelli relativi alla sintassi, ad esempio assicurarsi che le istruzioni SQL siano formate correttamente e che le variabili siano state dichiarate correttamente:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
SELECT @Bob;
ottiene il seguente errore:
Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "@Bob".
E il seguente:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
CREATE TABLE b
ottiene il seguente errore:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'b'.
Se il batch analizza correttamente, viene compilato, in quel momento vengono controllati elementi come le autorizzazioni e vengono eseguiti alcuni altri controlli.
-- 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;
L'SQL sopra è stato formato correttamente in modo che il batch venga analizzato correttamente. Ora prova a eseguire l'SQL sopra premendo F5 o Control-E o il ! Pulsante Esegui , ecc.
Questa volta viene visualizzato il seguente errore:
Msg 5058, Level 16, State 1, Line 4
Option 'RECOVERY' cannot be set in database 'tempdb'.
anche se IF (1 = 0)
garantisce che il codice non verrà mai eseguito. Ciò significa che stai riscontrando un errore di compilazione. È possibile aggirare questi tipi di errori spostando il codice offensivo in un sottoprocesso tramite una EXEC
chiamata.
Eseguire quanto segue e verrà completato correttamente poiché ciò che è all'interno di EXEC()
non viene analizzato o compilato fino a quando tale istruzione non viene eseguita in fase di esecuzione.
IF (1 = 0)
BEGIN
EXEC('
ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
');
END;
Per riassumere: in
primo luogo, considerare che sp_MSForEachDB
cicla attraverso i database ed esegue l'SQL passato dopo aver sostituito il ?
con il nome del database corrente. Quindi, quando il cursore all'interno di sp_MSForEachDB
arriva a [tempdb]
, fa effettivamente quanto segue:
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
In secondo luogo, ci sono diversi passaggi che SQL Server esegue quando si esegue un batch di query:
- Parse
- Compilare
- Esecuzione effettiva
È importante comprendere che si tratta di passaggi separati e che possono generare errori prima di procedere al passaggio successivo (e quindi annullare l'ulteriore elaborazione prima di passare al passaggio successivo). Nel caso qui, l'errore si sta verificando nel passaggio 2 - Compilare - come dimostrato dal 2 ° all'ultimo esempio sopra (il primo con cui iniziare IF (1 = 0)
). Le IF (1 = 0)
impedisce il codice all'interno del BEGIN...END
blocco da sempre in esecuzione, tuttavia l'errore si verifica ancora. Quindi l'errore non si verifica a causa di un tentativo effettivo di eseguire le due ALTER
istruzioni.
Il motivo per cui il wrapping delle ALTER
istruzioni all'interno della EXEC()
funzione funziona è perché SQL Server non analizzerà e compilerà ciò che è all'interno di EXEC()
finché non EXEC()
è effettivamente in esecuzione. A quel punto, la IF ( (select database_id from sys.databases where name = ''?'') > 4)
dichiarazione sarà consentita l'esecuzione in quanto il lotto non mancherà durante la compilazione e la farà ad esecuzione, e la IF
dichiarazione sarà saltare [tempdb]
e la " 'RECOVERY' opzione non può essere impostata nel database 'tempdb'" Errore non accadrà.