現時点では、SQL Serverがヒストグラムステップを部分的にカバーする範囲述語のカーディナリティを評価する方法を理解しようとしています。
インターネットの cardinality-estimation-for-and-for-intra-step-statistics-value で私は同様の質問に出くわし、Paul Whiteはかなり興味深い答えを出しました。
Paulの回答によると、述語> =および>(この場合、少なくとも120のカーディナリティ推定モデルのみに関心があります)のカーディナリティを推定する式は次のとおりです。
>の場合:
Cardinality = EQ_ROWS + (AVG_RANGE_ROWS * (F * (DISTINCT_RANGE_ROWS - 1)))
> =の場合:
Cardinality = EQ_ROWS + (AVG_RANGE_ROWS * ((F * (DISTINCT_RANGE_ROWS - 1)) + 1))
[Production]。[TransactionHistory]テーブルでこれらの数式の適用をAdventureWorks2014TransactionDate列を使用した範囲述語と '20140614'から '20140618'の間の日時範囲に基づくデータベース。
この範囲のヒストグラムステップの統計は次のとおりです。
式に従って、次のクエリのカーディナリティを計算しました。
SELECT COUNT(1)
FROM [AdventureWorks2014].[Production].[TransactionHistory]
WHERE [TransactionDate] BETWEEN '20140615 00:00:00.000' AND '20140616 00:00:00.000'
次のコードを使用して計算が実行されました。
DECLARE @predStart DATETIME = '20140615 00:00:00.000'
DECLARE @predEnd DATETIME = '20140616 00:00:00.000'
DECLARE @stepStart DATETIME = '20140614 00:00:00.000'
DECLARE @stepEnd DATETIME = '20140618 00:00:00.000'
DECLARE @predRange FLOAT = DATEDIFF(ms, @predStart, @predEnd)
DECLARE @stepRange FLOAT = DATEDIFF(ms, @stepStart, @stepEnd)
DECLARE @F FLOAT = @predRange / @stepRange;
DECLARE @avg_range_rows FLOAT = 100.3333
DECLARE @distinct_range_rows INT = 3
DECLARE @EQ_ROWS INT = 0
SELECT @F AS 'F'
--for new cardinality estimator
SELECT @EQ_ROWS + @avg_range_rows * (@F * (@distinct_range_rows - 1) + 1) AS [new_card]
計算後、次の結果が得られました。
式によると、150.5であることがわかりましたが、オプティマイザは述語を225.75行と推定し、述語の上部境界を「20140617」に変更すると、オプティマイザはすでに250.833行を評価しますが、 200.6666行。
この場合、Cardinality Estimatorはどのように評価しますか?引用された式の理解のどこかに間違いがあるのでしょうか?
SQL Serverは、さまざまな状況でさまざまな計算を使用します。あなたの範囲はステップ内に完全に含まれているため、あなたの例はリンクされたQ&Aとは異なります。ステップ境界を越えません。また、1つではなく2つの端を持つ区間でもあります。 BETWEEN
を書き込むことは、>=
および<=
を使用して2つの別個の述部を書き込むことと同じです。
この式は、予想される個別の値の数に対してステップ内で線形補間を実行するように変更され、1つではなく2つの範囲の端点が指定された(そしてヒストグラムステップ内に存在すると想定された)ことを反映しています。
質問で与えられたヒストグラムのステップを使用して:
BETWEEN '20140615' AND '20140616'
を使用したクエリの場合、計算は次のようになります。
DECLARE
@Q1 float = CONVERT(float, CONVERT(datetime, '2014-06-15')),
@Q2 float = CONVERT(float, CONVERT(datetime, '2014-06-16')),
@K1 float = CONVERT(float, CONVERT(datetime, '2014-06-14')),
@K2 float = CONVERT(float, CONVERT(datetime, '2014-06-18')),
@RANGE_ROWS float = 301,
@DISTINCT_RANGE_ROWS float = 3;
DECLARE
@S1 float = (@Q1 - @K1) / (@K2 - @K1),
@S2 float = (@Q2 - @K1) / (@K2 - @K1);
DECLARE
@F float = @S2 - @S1;
DECLARE
@AVG_RANGE_ROWS float = @RANGE_ROWS / @DISTINCT_RANGE_ROWS;
SELECT
@AVG_RANGE_ROWS * ((@F * (@DISTINCT_RANGE_ROWS - 2)) + 2);
...与える225.75@Q2
を'20140616'
から'20140617'
に変更すると、結果は250.833になります。
両方の結果は、質問で与えられたものと一致します。