NOTE AUX LECTEURS: Veuillez lire l'intégralité de la question, y compris l'exemple de code (c'est-à-dire pas seulement le titre). Cette question n'est pas sur la meilleure façon de parcourir les bases de données, ni sur la raison pour laquelle [tempdb] reçoit cette erreur. L'OP essaie déjà d'éviter d'exécuter les ALTER
instructions sur toutes les bases de données système (enfin, les 4 visibles) et demande pourquoi l'instruction IF qui devrait être sautée [tempdb] ne semble pas la sauter.
Pourquoi le script suivant me donne-t-il une erreur concernant tempdb?
La raison en est que l' IF
instruction n'affecte que ce qui se passe lorsque le code s'exécute réellement, mais SQL Server doit toujours analyser et compiler le lot avant de l'exécuter. Les erreurs d'analyse sont celles liées à la syntaxe, telles que la vérification que les instructions SQL sont correctement formées et que les variables ont été correctement déclarées:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
SELECT @Bob;
obtient l'erreur suivante:
Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "@Bob".
Et ce qui suit:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
CREATE TABLE b
obtient l'erreur suivante:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'b'.
Si le lot est analysé avec succès, il est compilé, auquel moment des éléments tels que les autorisations sont vérifiés et d'autres vérifications sont effectuées.
-- 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;
Le SQL ci-dessus est correctement formé afin que le batch soit correctement analysé. Essayez maintenant d'exécuter le SQL ci-dessus en appuyant sur F5 ou Control-E ou le ! Bouton Exécuter , etc.
Cette fois, vous obtenez l'erreur suivante:
Msg 5058, Level 16, State 1, Line 4
Option 'RECOVERY' cannot be set in database 'tempdb'.
même si le IF (1 = 0)
garantit que le code ne s'exécutera jamais. Cela signifie que vous rencontrez une erreur de compilation. Vous pouvez contourner ces types d'erreurs en déplaçant le code incriminé dans un sous-processus via un EXEC
appel.
Exécutez ce qui suit et cela se terminera avec succès car ce qui se trouve à l'intérieur de EXEC()
n'est pas analysé ou compilé tant que cette instruction n'est pas exécutée au moment de l'exécution.
IF (1 = 0)
BEGIN
EXEC('
ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
');
END;
Pour résumer:
Tout d'abord, considérez que sp_MSForEachDB
parcourt les bases de données et exécute votre SQL passé après avoir remplacé le ?
par le nom de la base de données actuelle. Ainsi, lorsque le curseur à l'intérieur de sp_MSForEachDB
arrive à [tempdb]
, il effectue effectivement les opérations suivantes:
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
Deuxièmement, SQL Server effectue plusieurs étapes lorsque vous exécutez un lot de requêtes:
- Analyser
- Compiler
- Exécution réelle
Il est important de comprendre que ce sont des étapes distinctes et peuvent générer des erreurs avant de passer à l'étape suivante (et donc annuler tout traitement supplémentaire avant de passer à l'étape suivante). Dans le cas ici, l'erreur se produit à l'étape 2 - Compilation - comme prouvé dans le deuxième au dernier exemple ci-dessus (le premier pour commencer IF (1 = 0)
). Le IF (1 = 0)
empêche le code à l'intérieur du BEGIN...END
bloc de s'exécuter, mais l'erreur persiste. Par conséquent, l'erreur ne se produit pas en raison d'une tentative réelle d'exécuter les deux ALTER
instructions.
La raison pour laquelle l'habillage des ALTER
instructions à l'intérieur de la EXEC()
fonction fonctionne parce que SQL Server n'analysera pas et ne compilera pas ce qui se trouve à l'intérieur de EXEC()
jusqu'à ce que le EXEC()
soit réellement exécuté. À ce moment - là, la IF ( (select database_id from sys.databases where name = ''?'') > 4)
déclaration sera autorisé à exécuter car le lot ne manquera pas lors de la compilation et la rendre à l' exécution, et la IF
déclaration sera sauter [tempdb]
et la « option « récupération » ne peut pas être défini dans la base de données « tempdb » » erreur n'arrivera pas.