web-dev-qa-db-ja.com

ヒストグラム外の基数推定

セットアップ

基数の見積もりを理解するのに問題があります。これが私のテスト設定です:

  • stack Overflowデータベースの2010バージョン
  • SQL Server 2017 CU15 + GDR(KB4505225)-14.0.3192.2
  • 新しいCE(互換性レベル140)

私はこのプロシージャを持っています:

_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]);
_

screenshot of statistics output in SSMS

そのハイライトは次のとおりです。

  • 統計のサンプルレートは1.81%とかなり低くなっています(67,796/3,744,192)。
  • 31のヒストグラムステップのみが使用されました
  • 「すべての密度」の値は_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計算機内でその選択性の値が生成される方法をリバースエンジニアリングすることはできませんでした。

14
Josh Darnell

私のテストに基づくと、範囲外のカーディナリティの推定値は、行数の平方根であり、最後の統計の更新以降に追加された行の数によって下に制限され、値ごとの平均行によって上に制限されます。

あなたの場合、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))

どこ

  • MCは「変更カウント」です(統計が作成されてからの変更の数)。
  • ACは「調整されたカーディナリティ」です(統計からの行数とMC)。
  • ARは値ごとの平均行です(統計の行数を列の個別の値で割ったもの)

これらの見積もりの​​計算式、および計算機に関するその他の詳細は、このブログ投稿にあります。 CSelCalcAscendingKeyFilter Calculatorからの見積もりの​​分析

13
Forrest