StackOverflowダンプを使用していくつかのテストを実行しています。
特に、私はこのテーブルをクエリしています:
私はこのインデックスを作成しました:
次のクエリを実行しています(インデックスに代替をテストさせるだけです)
次のexecプランは高コスト(66.63)になります。
これらは、このクエリを実行した後のIO統計です。
次に、直接の値の代わりに変数を指定して同じクエリを実行します
私はより良い計画を取得します(コストは0.4385)。
統計も優れています:
最初は... SQL Serverが直接の値をINTとして認識していないと思いましたが、型の不一致や暗黙の変換警告はありません。
並列処理を回避することも試みましたが、述語に直接値を渡すときに、MAXDOP 1を使用して高コストのプラン(およびそれ以上のIO統計))を取得します。
両方のプランを比較すると、異なる見積もりがあります。
述語の一部として渡される直接値の何が問題になっていますか?
述語の一部として渡される直接値の何が問題になっていますか?
オプティマイザは、変数の推定値を計算するときに統計密度ベクトルを使用します。
「直接」または「静的」の値がクエリに直接埋め込まれている場合は、代わりに統計ヒストグラムが使用されます。これが、異なる見積もり、つまり異なる計画を取得する理由です。
これが私の推定計画です: https://www.brentozar.com/pastetheplan/?id=SJCduTuKN
SOデータベースの2010年のコピーでは、OwnerUserId列の密度は.000003807058です。これに3,744,192行を掛けると= 14.2544行です。これは、IX_Posts_OwnerUserIdから取得されると推定される行数です。 。
この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);
_