SQL如何估计小于<谓词中的行数

SEa*_*986 4 statistics database-internals sql-server-2016 cardinality-estimates

我一直在做一些测试,试图更好地理解 SQL Server 如何使用直方图来估计与等式谓词以及 < 或 > 谓词匹配的行数

鉴于我正在使用AdventureWorks2016 OLTP 数据库

如果能够理解SQL Server对=和>谓词的估计过程:

/* update stats with fullscan first */    
UPDATE STATISTICS Production.TransactionHistory WITH FULLSCAN
Run Code Online (Sandbox Code Playgroud)

然后我可以看到该列的直方图TransactionHistory.Quantity

DBCC SHOW_STATISTICS (
    'Production.TransactionHistory', 
    'Quantity')
Run Code Online (Sandbox Code Playgroud)

下面的屏幕截图是我运行测试的直方图的顶端:

在此输入图像描述

以下查询将估计 6 行,因为谓词中的值是 RANGE_HI_KEY,因此对该存储桶使用 EQ_ROWS:

SELECT  * 
FROM    Production.TransactionHistory
WHERE   Quantity = 2863
Run Code Online (Sandbox Code Playgroud)

以下将估计 1.36 行,因为它不是 RANGE_HI_KEY,因此使用 AVG_RANGE_ROWS 作为它所属的存储桶:

SELECT  * 
FROM    Production.TransactionHistory
WHERE   Quantity = 2862
Run Code Online (Sandbox Code Playgroud)

以下“大于”查询将估计 130 行,这似乎是 RANGE_HI_KEY > 2863 的所有存储桶的 RANGE_ROWS 和 EQ_ROWS 之和

SELECT  * 
FROM    Production.TransactionHistory
WHERE   Quantity > 2863
Run Code Online (Sandbox Code Playgroud)

下面是类似的查询,但该值不是直方图中的 RANGE_HI_KEY。SQL Server 再次估计 130 并且似乎使用与上面相同的方法

SELECT  * 
FROM    Production.TransactionHistory
WHERE   Quantity > 2870 
Run Code Online (Sandbox Code Playgroud)

到目前为止,这一切都是有意义的,因此我的测试转移到“小于”查询

SELECT  * 
FROM    Production.TransactionHistory
WHERE   Quantity < 490 
Run Code Online (Sandbox Code Playgroud)

对于这个查询,SQL Server 估计有 109,579 行,但我不知道它从哪里得到的:

RANGE_HI_KEY + RANGE_ROWS 直到(包括 RANGE_HI_KEY 470)的所有存储桶 = 109,566,所以我们在某个地方缺少 11 个。

SQL Server 如何使用直方图来估计“小于”谓词将返回的行数

Pau*_*ite 6

对于这个查询,SQL Server 估计有109,579 行,但我不知道它从哪里得到的:

RANGE_HI_KEY + RANGE_ROWS 直到(包括 RANGE_HI_KEY 470)的所有存储桶 = 109,566,所以我们在某个地方缺少 11 个。

还差 13,而不是 11:109,579 - 109,566 = 13。

正如我的相关答案所示,总体思路是在部分步骤内使用线性插值,假设均匀性。

在你的情况下:

直方图片段

所以问题是,当假设这 23 个在直方图步长内均匀分布(500)时,RANGE_ROWS我们期望有多少个与谓词匹配:< 490RANGE_HI_KEY

DECLARE
    @ARR float = 23e0 / 6e0, -- AVG_RANGE_ROWS
    @DRR float = 6e0,        -- DISTINCT_RANGE_ROWS
    @PR float = 490 - 470,   -- predicate range
    @SR float = 499 - 470    -- whole step range (excluding high key)

SELECT (@DRR - 1) * ((@PR - 1) / @SR) / ((@SR - 1) / @SR) * @ARR;
Run Code Online (Sandbox Code Playgroud)

此计算得出13.00595

这些-1因素考虑使用<假定排除行的情况DISTINCT_RANGE_ROW。使用时<=,假定该行与谓词匹配。

整个事情是应用您所要求的范围的分数与直方图步骤覆盖的范围的修改。如果不排除不匹配的值,那就很简单了@PR/@SR