基数の見積もりを理解するのに問題があります。これが私のテスト設定です:
私はこのプロシージャを持っています:
_USE StackOverflow2010;
GO
CREATE OR ALTER PROCEDURE #sp_PostsByCommentCount
@CommentCount int
AS
BEGIN
SELECT *
FROM dbo.Posts p
WHERE
p.CommentCount = @CommentCount
OPTION (RECOMPILE);
END;
GO
_
_dbo.Posts
_テーブルには非クラスター化インデックスまたは統計情報はありません(Id
にはクラスター化インデックスがあります)。
この推定計画を求めると、_dbo.Posts
_から得られる「推定行」は1,934.99です。
_EXEC #sp_PostsByCommentCount @CommentCount = 51;
_
次の統計オブジェクトは、見積もりプランを要求したときに自動的に作成されました。
_DBCC SHOW_STATISTICS('dbo.Posts', [_WA_Sys_00000006_0519C6AF]);
_
そのハイライトは次のとおりです。
0.03030303
_です(33の異なる値がサンプリングされました)RANGE_HI_KEY
_は50で、_EQ_ROWS
_は1です50を超える値(2,147,483,647まで)を渡すと、1,934.99行の見積もりになります。 この推定値を生成するためにどの計算または値が使用されていますか?従来のカーディナリティ推定器は、1行の推定値を生成します。
ここに私が持っていたいくつかの理論、私が試したもの、またはこれを調べている間に掘り出すことができた追加の情報があります。
最初は密度ベクトルだと思っていましたが、OPTION (OPTIMIZE FOR UNKNOWN)
を使用した場合と同じです。しかし、この統計オブジェクトの密度ベクトルは3,744,192 * 0.03030303 = 113,460なので、そうではありません。
_query_optimizer_estimate_cardinality
_イベントを収集する拡張イベントセッション(Paul Whiteのブログ投稿 Cardinality Estimation:Combining Density Statistics )を実行してみたところ、次のような興味深い情報が得られました。
_<CalculatorList>
<FilterCalculator CalculatorName="CSelCalcColumnInInterval" Selectivity="-1.000"
CalculatorFailed="true" TableName="[p]" ColumnName="CommentCount" />
<FilterCalculator CalculatorName="CSelCalcAscendingKeyFilter" Selectivity="0.001"
TableName="[p]" ColumnName="CommentCount" UseAverageFrequency="true"
StatId="4" />
</CalculatorList>
_
したがって、CSelCalcAscendingKeyFilter
計算機が使用されたようです(他の計算機は、それが何であれ、失敗したと言います)。この列はキーではなく、一意でも、必ずしも昇順でもありませんが、何でもかまいません。
その用語をグーグルで操作すると、いくつかのブログ投稿が表示されます。
これらの投稿は、新しいCEが密度ベクトルと統計の修正カウンターの組み合わせに基づいて、これらのヒストグラム外推定値を基にしていることを示しています。残念ながら、私はすでに密度ベクトルを除外しています(そうですか?!)。修正カウンターは(とにかく_sys.dm_db_stats_properties
_ごとに)ゼロです。
Forrest 推定プロセスに関する詳細情報を得るためにTF 2363をオンにすることを提案しました。私はその出力から最も関連するものはこれだと思います:
_Plan for computation:
CSelCalcAscendingKeyFilter(avg. freq., QCOL: [p].CommentCount)
Selectivity: 0.000516798
_
これは画期的なことです(ありがとう、Forrest!):その_0.000516798
_の数値(上記のXE _Selectivity="0.001"
_属性では役に立たないように丸められているようです)に表の行数を掛けると、推定になります私は探していました(1,934.99)。
私はおそらく明らかなものを欠いていると思いますが、CSelCalcAscendingKeyFilter
計算機内でその選択性の値が生成される方法をリバースエンジニアリングすることはできませんでした。
私のテストに基づくと、範囲外のカーディナリティの推定値は、行数の平方根であり、最後の統計の更新以降に追加された行の数によって下に制限され、値ごとの平均行によって上に制限されます。
あなたの場合、1,934.99 = SQRT(3744192)
以下のテスト設定:
--setup
USE TestDB
ALTER DATABASE [TestDB] SET AUTO_UPDATE_STATISTICS OFF
GO
DROP TABLE IF EXISTS dbo.Hist
CREATE TABLE dbo.Hist (
ID int identity primary key,
Num int
)
INSERT dbo.Hist
SELECT TOP 300
(ROW_NUMBER() OVER(ORDER BY(SELECT 1/0)))%3
FROM master..spt_values a
CROSS JOIN master..spt_values b
--Get estimated plan
--don't forget to run right after setup to auto-create stats
SELECT *
FROM dbo.Hist
WHERE Num = 1000
--gradually add rows, then rerun estimate above
INSERT dbo.Hist
SELECT TOP 100
-1
FROM master..spt_values a
--I sure hope you weren't testing this in prod (cleanup)
ALTER DATABASE [TestDB] SET AUTO_UPDATE_STATISTICS ON
GO
驚くべきことに、このアプローチから生成された行の推定値も、合計で400行で20行、900行で30行、1600行で40などです。
ただし、10000を超えると、行の推定値は最大で100になります。これは、既存の統計の値ごとの行数です。 sqrt(300)> 10であるため、10行のみを追加すると、推定値は10に設定されます。
したがって、推定値は次の式を使用して表現できます。
Estimate = MIN(SQRT(AC), MIN(AR, MC))
統計がサンプリングされる場合、MCは考慮されないことに注意してください。したがって、式は次のようになります。
Estimate = MIN(SQRT(AC), AR))
どこ
これらの見積もりの計算式、および計算機に関するその他の詳細は、このブログ投稿にあります。 CSelCalcAscendingKeyFilter Calculatorからの見積もりの分析