web-dev-qa-db-ja.com

sp_MSForEachDBを使用してデータベースをループするときにIFステートメントがTempDBをスキップしない

[SQL Server 2012 SP2 EE]

次のスクリプトで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'

ここに私が得るエラーがあります:

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

本来の仕事をします。しかし、エラーの理由を考えることはできません。 tempdbのdatabaseIDが2であることはわかっていますが、少なくともtempdbのオプションを設定しようとするべきではありません。

8
GaganLamba

読者への注意:サンプルコードを含む質問全体を読んでください(つまり、タイトルだけではありません)。この質問は、ではありませんデータベースを循環する最良の方法について、また、[tempdb]がこのエラーを受け取る理由についても同様です。OPは、すべてのシステムデータベース(4つの表示可能なデータベース)でALTERステートメントの実行を回避しようとしており、IFステートメントが[tempdb]をスキップしているように見えます。

次のスクリプトで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は適切に形成されているため、バッチは正常に解析されます。 F5またはControl-Eまたは!Executeボタンなどを押して、上記のSQLを実行してみます。

今回は、次のエラーが発生します。

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が実行するいくつかの手順があります。

  1. 解析
  2. コンパイル
  3. 実際の実行

これらは個別のステップであり、次のステップに進む前にエラーが発生する可能性がある(したがって、次のステップに進む前に以降の処理をキャンセルする)ことを理解することが重要です。この場合、上記の2番目から最後の例(IF (1 = 0)で始まる最初の例)で証明されているように、ステップ2-コンパイル-でエラーが発生しています。 IF (1 = 0)は、BEGIN...ENDブロック内のコードが実行されないようにしますが、エラーは引き続き発生します。したがって、2つのALTERステートメントを実際に実行しようとしたため、エラーは発生していません。

EXEC()関数内でALTERステートメントをラップする理由は、SQL ServerがEXEC()内の内容をEXEC()まで解析およびコンパイルしないためです。 ]は実際に実行されています。その時点で、IF ( (select database_id from sys.databases where name = ''?'') > 4)ステートメントは、バッチがコンパイル中に失敗せずに実行されるため、実行が許可され、IFステートメント[tempdb]をスキップして、「オプション「RECOVERY」をデータベース「tempdb」に設定できません」エラーは発生しません。

4
Solomon Rutzky

メッセージ5058、レベル16、状態1、行5オプション「RECOVERY」をデータベース「tempdb」に設定できません。

本来の仕事をします。しかし、エラーの理由を考えることはできません。

まず最初に、ms_foreachdbを使用する必要はありません。その文書化されていないため、単純なカーソルを使用してループすることができます。エラーに関して、すべてのデータベースincluding tempdbの復旧モデルを変更しようとしていますが、tempdbの復旧モデルを変更することはできず、バックアップ操作も実行できないため、このエラーメッセージが表示されます。これはマイクロソフトでは許可されていません。 tempdbで実行できる 操作の詳細をお読みください

3
Shanky

Tempdbの回復オプションを変更できないという事実は別として、あなたはループは必要ありません何をしているのか:

を押してSSMSで実行する 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'
3
Kin Shah