直方图以外的基数估计


14

设定

我在了解基数估算值时遇到了一些麻烦。这是我的测试设置:

  • 2010版本的Stack Overflow数据库
  • 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]);

SSMS中统计信息输出的屏幕截图

其中的重点是:

  • 统计数据的抽样率很低,为1.81%(67,796 / 3,744,192)
  • 仅使用31个直方图步骤
  • “所有密度”值是0.03030303(采样了33个不同的值)
  • RANGE_HI_KEY直方图中的最后一个是50,其中EQ_ROWS1

传递任何大于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将这些直方图外部估计基于密度向量和stat的修改计数器的组合。不幸的是,我已经排除了密度矢量(我认为?!),并且修改计数器为零(sys.dm_db_stats_properties无论如何)。

跟踪标志

Forrest建议我打开TF 2363以获取有关估计过程的更多信息。我认为该输出中最相关的是:

Plan for computation:

  CSelCalcAscendingKeyFilter(avg. freq., QCOL: [p].CommentCount)

Selectivity: 0.000516798

这是一个突破(感谢Forrest!):这个0.000516798数字(似乎已经在Selectivity="0.001"上面的XE 属性中无助地舍入了)乘以表中的行数即是我一直在寻找的估计值(1,934.99)。

我可能缺少明显的东西,但是我无法对工程师内部如何生成选择值进行反向工程CSelCalcAscendingKeyFilter

Answers:


13

根据我的测试,越界基数估计只是行计数的平方根,其下限是自上次统计信息更新以来添加的行数,上限值是每个值的平均行数。

您的情况为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

出乎意料的是,通过这种方法生成的行估计数为:总行数为20,每行有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计算器分析估计

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.