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


8

[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のオプションを設定しようとするべきではありません。

回答:


4

読者への注記:コード例(タイトルだけでなく)を含め、質問全体をお読みください。この質問は、データベースを最適に循環する方法についてではなく、[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が実行するいくつかの手順があります。

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

これらは別個のステップであり、次のステップに進む前にエラーを生成する可能性がある(したがって、次のステップに進む前にそれ以上の処理をキャンセルする)ことを理解することが重要です。この場合、上記の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'に設定できません」エラーが発生します。発生しません。


私の質問は-「if」ステートメントがtempdbのID(つまり2)が4より大きいという条件を満たさない場合にifステートメントブロックに送信できる
理由

1
@GaganLamba私はあなたの質問を理解し、非常に具体的に答えました。私の例を実行しましたか?回答で述べたように、これはコンパイル時のエラーであり、実行時のエラーではありません。したがって、この時点では、IFステートメントも他のSQLステートメント(2つを含むALTER)も実行されていません。IFステートメントがされていないのtempdbのIDが内部にあるものに送信することが可能BEGIN/ ENDブロック、およびコードにも実行されていないALTERこの時点でステートメントを。実行前に SQLを検証しているため、SQL Serverによってエラーがスローされています。最後に概要セクションを追加しました。
ソロモンRutzky

3

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

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

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


1
ms_foreachdbは文書化されていないので使用しないでください。tempdbはバックアップまたは変更の回復オプションではないことも理解しています。しかし、SQLサーバーがTEMPDBのオプションを変更しようとしている理由について、私の質問にはまだ答えがありません。TEMPDBのID(2)は返されませんでした。
GaganLamba 2015年

1
@GaganLamba SQL Serverは、実際にはTEMPDBのオプションを変更しようとはしていません。ALTER記述は、単にされて検証され実行されません。私は私の答えで詳細を提供します
ソロモンRutzky

3

tempdbの回復オプションを変更できないという事実は別として、実行中の処理にループ必要ありません。

CTRL+ を押してSSMSで実行する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'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.