SEa*_*986 7 sql-server primary-key statistics index-statistics sql-server-2019
我有一个关于表中主键列的统计信息。当我使用默认选项更新统计信息时:
UPDATE STATISTICS dbo.MyTable PK__MyTable__CB394B3946083350
Run Code Online (Sandbox Code Playgroud)
我得到一个直方图如下(删节)
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
3400002201 0 1 0 1
3400009992 18103.04 1 7790 2.323882
3400040033 26083.68 1 26080 1.000144
3400050456 13029.09 1 10422 1.250153
3400087676 26083.68 1 26080 1.000144
3400103858 19556.38 1 16181 1.208602
3400126866 13029.09 1 13029 1
3400162832 39138.27 1 35965 1.088232
3400213115 45665.56 1 45641 1.000547
3400238444 26083.68 1 25328 1.029836
3400242626 13029.09 1 4181 3.116262
3400262174 19556.38 1 19547 1.00048
3400283983 26083.68 1 21808 1.19606
3400304837 19556.38 1 19556 1
3400316046 13029.09 1 11208 1.162481
3400346666 13029.09 1 13029 1
3400368443 19556.38 1 19556 1
3400385634 26083.68 1 17190 1.517375
3400390548 13029.09 1 4913 2.651962
3400398297 13029.09 1 7748 1.681607
3400417467 13029.09 1 13029 1
3400428728 13029.09 1 11260 1.157113
3400462206 32610.97 1 32600 1.000332
3400477978 13029.09 1 13029 1
3400492969 19556.38 1 14990 1.304629
3400507579 13029.09 1 13029 1
3400529627 32610.97 1 22047 1.479157
3400535909 13029.09 1 6281 2.074366
3400556632 26083.68 1 20722 1.258743
3400576037 19556.38 1 19404 1.007853
3400588565 19556.38 1 12527 1.561139
3400630507 39138.27 1 39120 1.000457
3400655236 19556.38 1 19556 1
3400670940 19556.38 1 15703 1.245392
3400691760 19556.38 1 19556 1
3400701959 19556.38 1 10198 1.917668
3400718913 19556.38 1 16953 1.153565
3400745176 19556.38 1 19556 1
Run Code Online (Sandbox Code Playgroud)
如果我们查看 Hi Key 3400009992,直方图告诉我们:
有一行等于该值 有 18,103 行的值 > 3400002201 且 < 3400009992,但是,在这 18,103 行中,只有 7,790 行是不同的。
怎么会这样?主键必须是唯一的
如果我使用 FULLSCAN 更新统计数据,我会得到下面的直方图(完整),它似乎准确地表示了数据
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
----------------------------------------------------------------------------------------------------------------------------------------------------------------
3400000000 0 1 0 1
3401474769 1474758 1 1474758 1
600004687218 16383 1 16383 1
600005089447 65535 1 65535 1
600005665352 98303 1 98303 1
600006294532 81919 1 81919 1
600008729190 294911 1 294911 1
600012125564 425983 1 425983 1
600014952842 376831 1 376831 1
600017609236 344063 1 344063 1
600017776836 24575 1 24575 1
600022385710 598015 1 598015 1
600022698873 38234 1 38234 1
600022698878 0 1 0 1
Run Code Online (Sandbox Code Playgroud)
为什么SQL Server的采样直方图不能代表主键的唯一性?
KB3202425 -改进:通过更改 UNIQUE 列上直方图的使用来提高 SQL Server 2016 的查询性能:
改进
在 Microsoft SQL Server 2016 当前的基数估计模型中,缩放直方图时不会解释唯一性。这可能会导致唯一列的频率大于1。RTM 中处理此问题的方法是完全忽略UNIQUE列上的直方图。当列的分布不均匀时,这可能会导致估计值不佳。
安装此更新后,如果直方图的所有步长值在唯一值的 10% 以内,则可以正确使用直方图。
笔记
仅当启用跟踪标志 4199、数据库配置 QUERY_OPTIMIZER_HOTFIXES 的数据库范围设置为 ON 或使用查询提示 ENABLE_QUERY_OPTIMIZER_HOTFIXES 时,才会启用此更改。
从 SQL Server 2016 SP1 开始,ENABLE_QUERY_OPTIMIZER_HOTFIXES 查询提示可用。
正如现代知识库中经常出现的那样,您必须想象它们真正想要传达的含义:
直方图缩放时无法解释唯一性
人们可以理解,这意味着采样统计直方图(这是唯一需要“缩放”的类型)不反映任何唯一性约束,这是您问题的核心。
如果将数据库兼容性级别设置为 120 或更早,则会出现有趣的差异。这显然不仅仅影响统计抽样。例如,在 SQL Server 2019 CU 19 上使用2010 Stack Overflow 示例数据库:
USE StackOverflow2010;
GO
ALTER DATABASE StackOverflow2010 SET COMPATIBILITY_LEVEL = 150;
GO
UPDATE STATISTICS dbo.Posts (PK_Posts__Id);
GO
SELECT TOP (5)
H.step_number,
H.range_high_key,
H.range_rows,
H.equal_rows,
H.distinct_range_rows,
H.average_range_rows
FROM sys.tables AS T
JOIN sys.schemas AS SCH
ON SCH.[schema_id] = T.[schema_id]
JOIN sys.indexes AS I
ON I.[object_id] = T.[object_id]
JOIN sys.stats AS S
ON S.[object_id] = T.[object_id]
AND S.stats_id = I.index_id
CROSS APPLY sys.dm_db_stats_histogram
(
S.[object_id],
S.stats_id
) AS H
WHERE
SCH.[name] = N'dbo'
AND T.[name] = N'Posts'
AND I.[type_desc] = N'CLUSTERED'
ORDER BY
H.step_number ASC;
Run Code Online (Sandbox Code Playgroud)
步骤号 | 范围高调 | 范围行数 | 相等行数 | 不同范围行 | 平均范围行数 |
---|---|---|---|---|---|
1 | 467 | 0 | 1 | 0 | 1 |
2 | 471 | 111.9387 | 1 | 3 | 37.31289 |
3 | 594 | 167.908 | 1 | 122 | 1.376295 |
4 | 5071 | 1735.05 | 1 | 1735 | 1 |
5 | 11035 | 3078.314 | 1 | 3078 | 1 |
将上述脚本中的兼容性级别150替换为120:
步骤号 | 范围高调 | 范围行数 | 相等行数 | 不同范围行 | 平均范围行数 |
---|---|---|---|---|---|
1 | 467 | 0 | 1 | 0 | 1 |
2 | 51352 | 25401.67 | 1 | 25402 | 1 |
3 | 115718 | 42914.28 | 1 | 42888 | 1.000605 |
4 | 141859 | 14267.46 | 1 | 14267 | 1 |
5 | 161001 | 14267.46 | 1 | 14267 | 1 |
注意range_rows
和distinct_range_rows
更接近于相等。
仔细想想,这可能是因为 CL 120 不允许并行统计更新,但无论如何我都会保留该示例。如果你搞乱了可选MAXDOP
子句 for ,你可以在 CL150 下看到类似的差异UPDATE STATISTICS
。
正如我一开始所说的,这是部分解释。只有能够访问源代码和内部设计文档的人才能准确地告诉您事情如何以及为何发生变化。
据推测,这都是正在进行的工作的一部分,目的是使选择性估计更加一致且更易于维护。我认为可以公平地说,到目前为止,这还不是一个彻底的成功故事。
相关问答: