database_scoped_configurationsのバグ


9

私は結果セットを挿入しようとしています:

SELECT * FROM sys.database_scoped_configurations

サーバー上のすべてのデータベースの設定を確認したいので、一時テーブルに入れます。だから私はこのコードを書きました:

DROP TABLE IF EXISTS #h
CREATE TABLE #h(dbname sysname, configuration_id INT, name sysname,     value SQL_VARIANT,  value_for_secondary SQL_VARIANT)
EXEC sys.sp_MSforeachdb 'USE ?; insert into #h(dbname, configuration_id, name, value,value_for_secondary)  SELECT ''?'' as dbname, * FROM sys.database_scoped_configurations  D'
SELECT * FROM #h H

ただし、データベースごとに1行しかなく、各データベースで単純な選択を実行すると予想される4行はありません。

これをコード化するには、sp_MSForEachDBを使用するよりも優れた方法があることを知っているので、いくつか試しました。しかし、データベースごとに1行しか取得できません。SQL Server 2016 RTMとSP1の両方でこれを試しました

これはSQL Server 2016のバグですか、それとも何か問題がありますか?


少なくともMicrosoft SQL Server 2017(RTM-CU15-GDR)でバグが修正されました
Henrik

回答:


8

これはSQL Server 2016のバグですか?

はい。間違いなくこれは正しい行動ではありません。私はそれをここ報告し、SQL Server 2016 SP2 CU9で修正されています

ミカエル・エリクソンは、コメントで言うsys.database_scoped_configurationssys.dm_exec_sessions形式のビューとして実装されています

SELECT ...  
FROM OpenRowset(TABLE xxxx)  

ただし、以下の2つのプランを比較すると明らかな違いがあります。

DBCC TRACEON(3604);

DECLARE @database_scoped_configurations TABLE(x INT);

INSERT INTO @database_scoped_configurations
SELECT configuration_id
FROM   sys.database_scoped_configurations
OPTION (QUERYTRACEON 8608, QUERYTRACEON 8615, QUERYTRACEON 8619, QUERYTRACEON 8620 );


DECLARE @dm_exec_sessions TABLE(x INT);

INSERT INTO @dm_exec_sessions
SELECT session_id
FROM   sys.dm_exec_sessions
OPTION (QUERYTRACEON 8608, QUERYTRACEON 8615, QUERYTRACEON 8619, QUERYTRACEON 8620 );

ここに画像の説明を入力してください

これらのクエリの両方のトレースフラグ8619の出力が示す

ルールの適用:EnforceHPandAccCard-x0->スプールまたはトップ(x0)

SQL Serverは、TVFのソースが挿入ターゲットでもないことを確認できないため、ハロウィーン保護が必要です。

セッションの場合、これは最初にすべての行をキャプチャするスプールとして実装されました。database_scoped_configurations追加することにより、TOP 1計画に。TOPハロウィーン保護のためのの使用については、この記事で説明します。記事には、ドキュメントに記載されていないトレースフラグについても言及TOPされています。

DECLARE @database_scoped_configurations TABLE(x INT);

INSERT INTO @database_scoped_configurations
SELECT configuration_id
FROM   sys.database_scoped_configurations
OPTION (QUERYTRACEON 8692)

TOP 1スプールではなく使用の明らかな問題は、挿入される行数を任意に制限することです。したがって、これは、関数によって返された行数が<= 1の場合にのみ有効です。

最初のメモはこんな感じ

ここに画像の説明を入力してください

これをクエリ2の最初のメモと比較してください

ここに画像の説明を入力してください

上記を正しく理解している場合、最初のTVFは最大で1行を返すことができるため、不適切な最適化が適用されます。2番目のクエリの最大値は1.34078E+1542^512)に設定されます。

この最大行数がどこから派生するのか私にはわかりません。おそらく、DMVの作成者によって提供されたメタデータですか?ハロウィーンの問題の発生を防ぐことができないため、TOP(50)回避策が書き直されないことも奇妙です(無期限に継続するのを止めます)TOP(1)TOP(50)


6

の使用を中止してくださいsp_MSForEachDB。これはサポートされておらず、文書化されておらず、バグがあります-これがここで問題になる可能性があります。私の交換はここで同じ問題を示していますが、一般的にはそれを使用する方が安全です。

このような場合、単一のコマンドをプロシージャに渡して複数回実行するよりも動的SQLを生成することをお勧めします(さらに信頼できる私のプロシージャでさえも)。彼らが彼らが言うことをすべてやろうとしていることを確認してください。

システムビューの基礎となるコードがを実装しているという観察を借りて、TOP (1)次の方法を試すことができます。

DROP TABLE IF EXISTS #h;

CREATE TABLE #h(dbname sysname, configuration_id INT, name sysname, 
  value SQL_VARIANT,  value_for_secondary SQL_VARIANT);

DECLARE @sql nvarchar(max) = N'', @base nvarchar(max) = N'insert into #h
  (dbname, configuration_id, name, value,value_for_secondary)  SELECT TOP ($c$) 
  $db$ as dbname, * FROM $qdb$.sys.database_scoped_configurations;';

SELECT @sql += REPLACE(REPLACE(REPLACE(@base, N'$qdb$', QUOTENAME(name)), 
  N'$db$', CHAR(39) + name + CHAR(39)), N'$c$', RTRIM(COUNT(*) OVER()))
FROM sys.databases WHERE state = 0;

PRINT @sql;
EXEC sys.sp_executesql @sql;
SELECT * FROM #h;

USEここでは使用していませんが、sysカタログビューの前にデータベース名を付けています。

ビューがなぜ魔法のように機能するのか、私にはわかりません。Microsoft(またはソースコードにアクセスできるか、デバッガを起動する意思がある人)からのコメントが必要になる可能性があるため、ここで良い答えが得られるかどうかはわかりません。


これは私が試したいくつかの方法の最初のものでしたが、例ではそのsprocを使用できるとは思いませんでした。
Henrik Staun Poulsen 2016

6

この問題をご報告いただきありがとうございます。

これは確かに、クエリオプティマイザーがsys.database_scoped_configurationsカタログビューのプランを生成する方法のバグです。SQL Server 2016の次の更新の1つとAzure SQL Databaseでこれに対処します。

回避策として、挿入TOPSELECT一部に句を追加して、正しい計画を取得できます。例:

DECLARE @database_scoped_configurations TABLE(x INT); 
INSERT INTO @database_scoped_configurations 
SELECT **TOP 100** configuration_id 
FROM sys.database_scoped_configurations 

3

これは非常に奇妙で潜在的なバグであることに同意しますが、たとえばTOP(50)をselectに追加すると、実際にはすべての行が返されるので、少なくともうまくいくでしょう。結果はシステムテーブル値関数([DB_SCOPED_CONFIG])からのものであるように見えるため、何が起こっているのか実際にはわかりません。

私はこのスレッドを監視して、「賢い」人々がなぜこれが起こっているのかを知っているかどうかを確認します。


各データベースのMAXDOP行のみを取得しますか?
Dan Guzman

@DanGuzman-はい選択自体は正常に動作します(すべてのforeachを使用せず、単一のデータベースでのみ)。Insert intoを追加すると、奇妙な動作が発生します
Scott Hodgin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.