web-dev-qa-db-ja.com

SQL Serverは、述語が相関していることをどのようにして知るのですか?

カーディナリティの見積もりが悪い(単純なインデックス付け、最新の統計などにもかかわらず)SQLクエリ2008 SQL R2クエリを診断しているため、クエリプランが不十分であるにもかかわらず、おそらく関連するKB記事が見つかりました: FIX:パフォーマンスの低下SQL Server 2008またはSQL Server 2008 R2またはSQL Server 2012で相関AND述語を含むクエリを実行した場合

私は、KB記事が「相関する」とはどういう意味かを推測できます。述語#2と述語#1は主に同じ行を対象としています。

しかし、SQL Serverがこれらの相関関係をどのように認識しているかはわかりません。テーブルには、両方の述語の列を含む複数列のインデックスが必要ですか? SQLは統計を使用して、ある列の値が別の列に相関しているかどうかを確認しますか?または、他の方法が使用されていますか?

私はこれを2つの理由で尋ねています:

  1. この修正プログラムを使用して改善できる可能性のあるテーブルとクエリを特定するには
  2. #1に影響を与えるためにインデックス付け、統計などで何をすべきかを知る
15
Justin Grant

以下に示す単純な AdventureWorks クエリと実行プランを検討してください。クエリには、ANDで接続された述語が含まれています。オプティマイザのカーディナリティの見積もりは41,211行です。

-- Estimate 41,211 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';

Default execution plan

デフォルトの統計を使用する

単一列の統計のみが与えられた場合、オプティマイザは、各述語のカーディナリティを個別に推定し、結果の選択性を掛け合わせることにより、この推定を生成します。このヒューリスティックは、述部が完全に独立していることを前提としています。

クエリを2つの部分に分割すると、計算が見やすくなります。

-- Estimate 68,336.4 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336;

トランザクション履歴テーブルには合計で113,443行が含まれているため、68,336.4の見積もりは、この述語の68336.4/113443 =0.60238533の選択性を表しています。この推定値は、TransactionID列のヒストグラム情報と、クエリで指定された定数値を使用して取得されます。

-- Estimate 68,413 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';

この述部の推定選択性は68413.0/113443 =0.60306056です。この場合も、述語の定数値とTransactionDate統計オブジェクトのヒストグラムから計算されます。

述語が完全に独立していると仮定すると、2つの述語を掛け合わせることで、それらの選択性を一緒に推定できます。最終的なカーディナリティの見積もりは、結果の選択性にベーステーブルの113,443行を掛けることによって得られます。

0.60238533 * 0.60306056 * 113443 =41210.987

四捨五入後、これは元のクエリで見られる41,211の推定値です(オプティマイザも内部で浮動小数点演算を使用します)。

大した見積もりではない

TransactionID列とTransactionDate列は、AdventureWorksデータセット内で密接な相関関係があります(単調に増加するキーと日付列がよくあるように)。この相関関係は、独立性の仮定に違反していることを意味します。結果として、実行後のクエリプランには、推定41,211ではなく68,095行が表示されます。

Post-execution plan

トレースフラグ4137

このトレースフラグを有効にすると、述語の組み合わせに使用されるヒューリスティックが変更されます。オプティマイザーは、完全な独立性を想定する代わりに、2つの述部の選択性が十分に近いため、相互に関連している可能性が高いと見なします。

-- Estimate 68,336.4
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13'
OPTION (QUERYTRACEON 4137);

TransactionID述語だけで68,336.4行と推定され、TransactionDate述語だけで68,413行と推定されたことを思い出してください。オプティマイザは、選択性を乗算するのではなく、これら2つの推定値のうち低い方を選択しました。

もちろん、これは異なるヒューリスティックですが、相関するAND述部を持つクエリの推定を改善するのに役立ちます。各述語は可能な相関について検討され、多くのAND句が含まれる場合は他の調整が行われますが、その例はその基本を示すのに役立ちます。

複数列の統計

これらは相関のあるクエリに役立ちますが、ヒストグラム情報は統計の先頭列のみに基づいています。したがって、以下の候補となる複数列統計は、重要な点で異なります。

CREATE STATISTICS
    [stats Production.TransactionHistory TransactionID TransactionDate]
ON Production.TransactionHistory
    (TransactionID, TransactionDate);

CREATE STATISTICS
    [stats Production.TransactionHistory TransactionDate TransactionID]
ON Production.TransactionHistory
    (TransactionDate, TransactionID);

それらのうちの1つだけを取ると、唯一の追加情報は「すべて」の密度の追加レベルであることがわかります。ヒストグラムには、TransactionDate列に関する詳細情報のみが含まれています。

DBCC SHOW_STATISTICS
    (
        'Production.TransactionHistory', 
        'stats Production.TransactionHistory TransactionDate TransactionID'
    );

Multi-column statstics

これらの複数列の統計を配置すると...

SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';

...実行プランは、単一列の統計情報のみが利用可能であった場合とまったく同じの見積もりを示します。

Multi-column statistics plan

20
Paul White 9