Istruzione IF che non salta TempDB durante il ciclo attraverso i database con sp_MSForEachDB


8

[SQL Server 2012 SP2 EE]

Perché il seguente script mi ​​dà un errore relativo a 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'

Ecco l'errore che ottengo:

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

Fa il lavoro che dovrebbe fare. Ma non riesco a pensare a una ragione dell'errore. So che il databaseID del tempdb è 2, quindi almeno non dovrebbe provare a impostare l'opzione per tempdb.

Risposte:


4

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 ALTERistruzioni 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' IFistruzione 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 EXECchiamata.

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_MSForEachDBcicla 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_MSForEachDBarriva 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:

  1. Parse
  2. Compilare
  3. 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...ENDblocco 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 ALTERistruzioni.

Il motivo per cui il wrapping delle ALTERistruzioni 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 IFdichiarazione sarà saltare [tempdb]e la " 'RECOVERY' opzione non può essere impostata nel database 'tempdb'" Errore non accadrà.


La mia domanda è: perché è l'istruzione "if" che consente di inviare l'ID tempdb (ovvero 2) al blocco
dell'istruzione

1
@GaganLamba Capisco la tua domanda e ho risposto in modo molto preciso. Hai eseguito i miei esempi? Come ho affermato nella risposta, si tratta di un errore di compilazione, non di runtime. Quindi né l' IFistruzione né altre istruzioni SQL (incluse le due ALTERs) vengono eseguite a questo punto. L' IFistruzione non consente l'invio dell'ID tempdb a ciò che si trova all'interno del blocco BEGIN/ END, e il codice non sta nemmeno eseguendo le ALTERistruzioni a questo punto. L'errore viene generato da SQL Server in quanto sta convalidando l'SQL prima di eseguirlo. Ho aggiunto una sezione di riepilogo alla fine.
Solomon Rutzky,

3

Messaggio 5058, livello 16, stato 1, riga 5 Non è possibile impostare l'opzione 'RECOVERY' nel database 'tempdb'.

Fa il lavoro che dovrebbe fare. Ma non riesco a pensare a una ragione dell'errore.

Prima di tutto non c'è bisogno di usare i ms_foreachdbsuoi documenti privi di documenti e molto cattivi in ​​cui è possibile eseguire il loop utilizzando un semplice cursore. Per quanto riguarda l'errore si sta tentando di modificare il modello di recupero di tutto il database including tempdbma non è possibile modificare il modello di recupero di tempdb né è possibile eseguire alcuna operazione di backup su di esso, motivo per cui è stato visualizzato questo messaggio di errore. Questo non è consentito da Microsoft. Leggi di più sulle operazioni che puoi eseguire su tempdb


1
Ho capito che ms_foreachdb non è documentato e non dovrei usarlo. Capisco anche che tempdb non dovrebbe essere sottoposto a backup o modificare l'opzione di ripristino. Ma la mia domanda è ancora senza risposta sul perché SQL Server sta cercando di modificare l'opzione per TEMPDB? L'ID TEMPDB, che è 2, non è stato nemmeno restituito.
GaganLamba,

1
@GaganLamba SQL Server non sta effettivamente cercando di modificare l'opzione per TEMPDB. Le ALTERdichiarazioni vengono semplicemente convalidate , non eseguite. Fornisco dettagli nella mia risposta .
Solomon Rutzky,

3

A parte il fatto che non puoi cambiare l'opzione di recupero per tempdb, non hai bisogno di un ciclo per quello che stai facendo:

Esegui in SSMS premendo CTRL+T

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'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.