NOTA PARA LOS LECTORES: Lea la pregunta completa, incluido el código de ejemplo (es decir, no solo el título). Esta pregunta no se trata de cómo recorrer mejor las bases de datos, ni se trata de por qué [tempdb] recibe este error. El OP ya está tratando de evitar ejecutar las ALTER
declaraciones en todas las bases de datos del sistema (bueno, las 4 visibles) y pregunta por qué la declaración IF que debería omitirse [tempdb] no parece omitirla.
¿Por qué el siguiente script me da un error relacionado con tempdb?
La razón es que la IF
declaración solo afecta lo que sucede cuando el código se está ejecutando, pero SQL Server todavía tiene que analizar y compilar el lote antes de ejecutarlo. Los errores de análisis son los relacionados con la sintaxis, como asegurarse de que las instrucciones SQL estén formadas correctamente y que las variables se hayan declarado correctamente:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
SELECT @Bob;
obtiene el siguiente error:
Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "@Bob".
Y lo siguiente:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
CREATE TABLE b
obtiene el siguiente error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'b'.
Si el lote se analiza con éxito, se compila, en cuyo momento se verifican los permisos y se realizan otras verificaciones.
-- 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;
El SQL anterior está formado correctamente para que el lote se analice correctamente. ¡Ahora intente ejecutar el SQL anterior presionando F5 o Control-E o el ! Botón de ejecución , etc.
Esta vez obtienes el siguiente error:
Msg 5058, Level 16, State 1, Line 4
Option 'RECOVERY' cannot be set in database 'tempdb'.
aunque IF (1 = 0)
asegura que el código nunca se ejecutará. Esto significa que se encuentra con un error de compilación. Puede sortear estos tipos de errores moviendo el código ofensivo a un subproceso a través de una EXEC
llamada.
Ejecute lo siguiente y se completará con éxito ya que lo que está dentro del EXEC()
archivo no se analiza ni se compila hasta que esa instrucción se ejecuta en tiempo de ejecución.
IF (1 = 0)
BEGIN
EXEC('
ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
');
END;
Para resumir:
Primero, considere que sp_MSForEachDB
recorre las bases de datos y ejecuta su SQL pasado después de reemplazarlo ?
con el nombre de la base de datos actual. Entonces, cuando el cursor dentro de sp_MSForEachDB
llega [tempdb]
, efectivamente hace lo siguiente:
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
Segundo, hay varios pasos que SQL Server toma cuando ejecuta un lote de consulta:
- Analizar gramaticalmente
- Compilar
- Ejecución real
Es importante comprender que estos son pasos separados y pueden generar errores antes de proceder al siguiente paso (y, por lo tanto, cancelar el procesamiento adicional antes de pasar al siguiente paso). En el caso aquí, el error ocurre en el Paso 2 - Compilar - como se demostró en el segundo al último ejemplo anterior (el primero para comenzar IF (1 = 0)
). Esto IF (1 = 0)
evita que el código dentro del BEGIN...END
bloque se ejecute, pero aún se produce el error. Por lo tanto, el error no ocurre debido a un intento real de ejecutar las dos ALTER
declaraciones.
La razón por la que funciona el encapsulado de las ALTER
declaraciones dentro de la EXEC()
función es porque SQL Server no analizará ni compilará lo que está dentro de la función EXEC()
hasta EXEC()
que realmente se esté ejecutando. En ese punto, IF ( (select database_id from sys.databases where name = ''?'') > 4)
se permitirá que la instrucción se ejecute, ya que el lote no fallará durante la compilación y llegará a la ejecución, y la IF
instrucción se saltará [tempdb]
y la "Opción 'RECUPERACIÓN' no se puede establecer en la base de datos 'tempdb'" error no ocurrirá.