web-dev-qa-db-ja.com

述語の直接の値はあまり良くない計画を生み出します

StackOverflowダンプを使用していくつかのテストを実行しています。

特に、私はこのテーブルをクエリしています:

Posts Table

私はこのインデックスを作成しました:

Created index

次のクエリを実行しています(インデックスに代替をテストさせるだけです)

Query with direct values

次のexecプランは高コスト(66.63)になります。

Not-so-good plan

これらは、このクエリを実行した後のIO統計です。

enter image description here

次に、直接の値の代わりに変数を指定して同じクエリを実行します

Query with variables

私はより良い計画を取得します(コストは0.4385)。

Better plan

統計も優れています:

Better statistics

最初は... SQL Serverが直接の値をINTとして認識していないと思いましたが、型の不一致や暗黙の変換警告はありません。

並列処理を回避することも試みましたが、述語に直接値を渡すときに、MAXDOP 1を使用して高コストのプラン(およびそれ以上のIO統計))を取得します。

両方のプランを比較すると、異なる見積もりがあります。

enter image description here

述語の一部として渡される直接値の何が問題になっていますか?

3
dsuy

述語の一部として渡される直接値の何が問題になっていますか?

変数とパラメーターの違い

オプティマイザは、変数の推定値を計算するときに統計密度ベクトルを使用します。

「直接」または「静的」の値がクエリに直接埋め込まれている場合は、代わりに統計ヒストグラムが使用されます。これが、異なる見積もり、つまり異なる計画を取得する理由です。

これが私の推定計画です: https://www.brentozar.com/pastetheplan/?id=SJCduTuKN

SOデータベースの2010年のコピーでは、OwnerUserId列の密度は.000003807058です。これに3,744,192行を掛けると= 14.2544行です。これは、IX_Posts_OwnerUserIdから取得されると推定される行数です。 。

screenshot of the index seek estimate

このDBCCコマンドを実行すると、そのインデックスの統計に関するこの情報を取得できます。

_DBCC SHOW_STATISTICS('dbo.Posts', 'IX_Posts_OwnerUserId');
_

これは(省略された)出力です:

_Name                    Updated             Rows
IX_Posts_OwnerUserId    Apr  8 2019  8:33AM 3744192

All density     Average Length  Columns
3.807058E-06    4               OwnerUserId
_

PostTypeIdもWHERE句の一部であるため、その列の統計も自動的に生成されます。その密度ベクトルは、0.25 x 3,744,192行= 936,048行になります。

_DBCC SHOW_STATISTICS('dbo.Posts', '_WA_Sys_00000010_0519C6AF');
_

そして出力:

_Name                        Updated             Rows
_WA_Sys_00000010_0519C6AF   Apr  8 2019  9:04AM 3744192

All density Average Length  Columns
0.25        4               PostTypeId
_

これは「AND」述部であるため、推定では2つのうち低い方を使用します。

変数の代わりに静的な値を使用すると、統計ヒストグラムが使用されます。これは、そのSHOW_STATISTICSコマンドの3番目の結果セットにあります。使用しているキーのヒストグラムエントリは次のとおりです。

_RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
22656           13040       11371   305                 42.7541
_

これは、11,371の見積もりが「静的な値」計画から来るところです。

ヒストグラムは、エッジのケースを少しよく処理するため、多くの場合、より適切な推定値になる可能性があります。このような大きなテーブルには、しばしばいくつかの異常値が存在するためです。

コストの違い

この特定のケースでは、ヒストグラムはexactlyrightである推定を生成します。作成される計画のコストは、密度ベクトルを使用するものよりも(正しく)高くなります。これは、より多くの行を処理する必要があるためです。

「低コスト」プランでは、実際に11,371行が生成されるときに、そのシークによって14行が生成されると考えています。

論理読み取り

ネストされたループのプリフェッチ のため、論理読み取りは並列プランでわずかに高くなります。私のマシンでは大きな違いはないようです-クエリの経過時間は互いに10ミリ秒以内でした。

すべての行が(とにかく私のマシン上で)1つのスレッドで終わるため、並列処理は実際には何の助けにもなりません。 OPTION (MAXDOP 1)を追加すると、実行時間は短縮されますが、余分な論理読み取りは削除されません。

このクエリの「余分な読み取り」の問題に対する1つの潜在的な解決策は、PostTypeIdを含まれる列として追加することにより、キー検索を完全に回避することです。

_CREATE INDEX IX_Posts_OwnerUserId ON dbo.Posts (OwnerUserId) 
INCLUDE (PostTypeId) 
WITH (DROP_EXISTING = ON);
_
4
Josh Darnell