4

次のクエリのパフォーマンスについて質問があります。

DECLARE @detail_level INT = 1,
        @DETAIL_1     INT = 1,
        @DETAIL_2     INT = 2,
        @DETAIL_3     INT = 4

SELECT mtbl.*,
       CASE
         WHEN @detail_level & @DETAIL_1 <> 0 THEN tbl_1.value
         ELSE NULL
       END,
       CASE
         WHEN @detail_level & @DETAIL_2 <> 0 THEN tbl_2.value
         ELSE NULL
       END,
       CASE
         WHEN @detail_level & @DETAIL_3 <> 0 THEN tbl_3.value
         ELSE NULL
       END
FROM   mtbl
       LEFT OUTER JOIN tbl_1
         ON @detail_level & @DETAIL_1 <> 0
            AND mtbl.KEY = tbl_1.KEY
       LEFT OUTER JOIN tbl_2
         ON @detail_level & @DETAIL_2 <> 0
            AND mtbl.KEY = tbl_2.KEY
       LEFT OUTER JOIN tbl_3
         ON @detail_level & @DETAIL_3 <> 0
            AND mtbl.KEY = tbl_3.KEY
WHERE  mtbl.KEY = @something 

クエリエンジンは、詳細レベルフィルタを使用してクエリを最適化し、これらの詳細テーブル列に対する不要なテーブル結合と不要なテーブル行アクセスを回避しますか?

はいの場合、クエリエンジンはそれらの不要なテーブルのインデックスロックを取得しませんか?

SQL Server 2008でクエリプランをキャプチャしましたが、詳細レベルが一致していなくても、インデックス検索操作はまだ存在していました。ただし、詳細が少ないクエリは、詳細が完全なクエリ(データ量が約500,000)よりも高速でした(約30%)。

4

2 に答える 2

4

実行計画を確認する必要があります。次のサンプルテーブルを作成すると

CREATE TABLE tbl_1([KEY] INT PRIMARY KEY, value INT)
CREATE TABLE tbl_2([KEY] INT PRIMARY KEY, value INT)
CREATE TABLE tbl_3([KEY] INT PRIMARY KEY, value INT)
CREATE TABLE mtbl([KEY] INT PRIMARY KEY, value INT)

INSERT INTO mtbl VALUES(1,1),(2,2) 
INSERT INTO tbl_1 VALUES(1,1),(2,2)
INSERT INTO tbl_2 VALUES(1,1),(2,2)
INSERT INTO tbl_3 VALUES(1,1),(2,2)

クエリは実行プランを提供します

プラン

これは、同じコストの4つのテーブルすべてに対する操作を示しているように見えますが、これはやや誤解を招く可能性があります実際にアクセスされるのはそのうちの2つだけです。(実際の実行計画でも示されているコストは、単なる推定コストです。)

実行後SET STATISTICS IO ON;、サンプルクエリは次のようになります

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tbl_1'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'mtbl'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

ご覧のとおり、結果にはどちらtbl_2も含まれていません。tbl_3インデックスシークオンのプロパティを見ると、tbl_3この演算子は実行されなかったことがわかります(実行数= 0)

求める

これは、外部結合がスタートアップ述部を持つフィルターの下にあり、それがtrueと評価された場合にのみ呼び出されるためです。

フィルター

ロックについての質問に関しては、行またはページのロックは適用されません tbl_2が、オブジェクトに対してはロックが解除されますtbl_3ISこれは実行することで見ることができます

DBCC TRACEON(1200,3604,-1)
/*Your query*/ 
DBCC TRACEOFF(1200,3604,-1)

ISロックは、オブジェクトのスキーマ変更、一括更新、または排他的ロックと互換性がありません。

于 2012-09-09T17:17:20.370 に答える
1

SQL Serverは、パラメーターの任意の値に対して機能するクエリプランを作成します。ただし、検出したパラメーターの最初のバージョンに合わせて最適化されます。これは「パラメータスニッフィング」と呼ばれます。

option (recompile)クエリヒントを使用して強制的に再コンパイルできます。再コンパイルするとクエリ時間が20〜30ミリ秒長くなりますが、クエリプランは特定のパラメータ値に合わせて最適化されます。

もう1つのオプションは、動的SQLを使用して、必要なテーブルのみを結合するクエリを生成することです。これにより、複数のキャッシュされた実行プランが作成されます。これは最高のパフォーマンスを発揮しますが、保守が困難になるという犠牲を伴います。

補足として、左側に結合されたテーブルの列は、条件nullに一致する行がない場合に設定されonます。だからあなたは短くすることができます:

case when @detail_level & @DETAIL_1 <> 0 then tbl_1.value else null end

に:

tbl_1.value
于 2012-09-09T14:54:29.857 に答える