読者への注記:コード例(タイトルだけでなく)を含め、質問全体をお読みください。この質問は、データベースを最適に循環する方法についてではなく、[tempdb]がこのエラーを受け取る理由についても同じです。OPはすでにALTER
すべてのシステムデータベース(4つの表示可能なデータベース)でステートメントを実行しないようにしており、[tempdb]をスキップする必要があるIFステートメントがスキップしていないように見える理由を尋ねています。
次のスクリプトでtempdbに関するエラーが発生するのはなぜですか?
その理由は、IF
ステートメントはコードが実際に実行されているときに発生することにのみ影響しますが、SQL Serverはそれを実行する前にバッチを解析およびコンパイルする必要があるためです。解析エラーは、SQLステートメントが適切に形成されていること、変数が適切に宣言されていることなど、構文に関連するエラーです。
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
SELECT @Bob;
次のエラーが発生します。
Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "@Bob".
そして次のように:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
CREATE TABLE b
次のエラーが発生します。
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'b'.
バッチが正常に解析されると、バッチがコンパイルされます。その時点で、アクセス許可などのチェックが行われ、その他のチェックが実行されます。
-- 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;
上記のSQLは適切に形成されているため、バッチは正常に解析されます。今押すことで上記のSQLを実行しようとF5またはコントロール-Eや!実行ボタンなど
今回は、次のエラーが発生します。
Msg 5058, Level 16, State 1, Line 4
Option 'RECOVERY' cannot be set in database 'tempdb'.
でもIF (1 = 0)
、コードが実行されることはありません。これは、コンパイルエラーが発生していることを意味します。これらのタイプのエラーは、呼び出しによって問題のコードをサブプロセスに移動することで回避できEXEC
ます。
以下を実行すると、EXEC()
そのステートメントが実行時に実行されるまで、内部が解析またはコンパイルされないため、正常に完了します。
IF (1 = 0)
BEGIN
EXEC('
ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
');
END;
まとめる
と、最初に、を現在のデータベース名でsp_MSForEachDB
置き換えた後、データベースを循環して渡されたSQLを実行することを検討してください?
。したがって、内部のカーソルがにsp_MSForEachDB
到達すると[tempdb]
、効果的に次のことが行われます。
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
次に、クエリバッチを実行するときにSQL Serverが実行するいくつかの手順があります。
- 解析
- コンパイル
- 実際の実行
これらは別個のステップであり、次のステップに進む前にエラーを生成する可能性がある(したがって、次のステップに進む前にそれ以上の処理をキャンセルする)ことを理解することが重要です。この場合、上記の2番目から最後の例(最初のIF (1 = 0)
)で証明されているように、ステップ2-コンパイル-でエラーが発生しています。これIF (1 = 0)
により、BEGIN...END
ブロック内のコードが実行されなくなりますが、エラーは引き続き発生します。したがって、実際には2つのALTER
ステートメントを実行しようとしたため、エラーは発生していません。
関数のALTER
内部でステートメントをラップすることがEXEC()
機能するのは、SQL Serverがが実際に実行さEXEC()
れるまで、内部の内容を解析およびコンパイルしないためEXEC()
です。その時点で、IF ( (select database_id from sys.databases where name = ''?'') > 4)
コンパイル中にバッチが失敗せずに実行されるため、ステートメントの実行が許可され、IF
ステートメントはスキップされ[tempdb]
、「オプション 'RECOVERY'をデータベース 'tempdb'に設定できません」エラーが発生します。発生しません。