我正在调整一个很慢的查询,我将问题的根源缩小到执行计划的最开始,其中 SQL Server 对支持左反连接的 WHERE IS NULL 过滤器做出了错误的估计 - SQL Server 估计 1 行并倾向于通过嵌套循环进行一些索引扫描,认为它只会执行一次,而事实上它发生了几千次:
我已设法创建 MCVE 来复制该问题。
设置测试环境
/* INSERT 35000 dinstinct random numbers into a table */
CREATE TABLE #TableA
(
ID BIGINT NULL
)
INSERT INTO #TableA
SELECT DISTINCT
TOP 35000
a.Random
FROM (
SELECT TOP 50000
ABS(CHECKSUM(NewId())) % 20000000 AS Random
FROM sys.messages
) a
GO
/* add a further 15000 that already exist in the table. Use a loop to increase the possibility of duplicates */
INSERT INTO #TableA
SELECT TOP 1000
ID
FROM #TableA a
ORDER BY NEWID()
GO 15
/* Insert 10000 numbers into another table, that are in the first table */
CREATE TABLE #TableB
(
ID BIGINT NOT NULL
)
INSERT INTO #TableB
SELECT TOP 10000
*
FROM #TableA
/* insert 80000 distinct random numbers that are not in the first table */
INSERT INTO #TableB
SELECT DISTINCT
TOP 80000
a.Random
FROM (
SELECT TOP 100000
ABS(CHECKSUM(NewId())) % 2000000 AS Random
FROM sys.messages
) a
LEFT JOIN #TableA b
ON a.Random = b.ID
WHERE b.ID IS NULL
那么,出现问题的查询是
SELECT a.ID
FROM #TableA a
LEFT JOIN #TableB b
ON a.ID = b.ID
WHERE b.ID IS NULL
这是一个相当简单的“显示 TableA 中所有不在 TableB 中的 ID”
我的测试环境的执行计划如下
我们可以看到与上面的计划非常相似的事情,就过滤运算符而言 - SQL Server 将两个表连接在一起,然后过滤出左表中但不在右表中的记录,它大大低估了与该谓词匹配的行数
如果我强制使用遗留估计,我会对操作员得到更好的估计
我认为旧估计量和新估计量之间的一个主要区别是它们对两个谓词之间相关性的假设有何不同——旧估计量假设两个谓词之间相关性很小,而新估计量则更为乐观,假设相关性更高?
我的问题是
- 什么原因导致对较新的基数估计量的低估?
- 除了强制使用旧的兼容模型之外,还有其他方法可以解决这个问题吗?
默认 CE 使用对最小/最大直方图值的粗对齐
。 旧版 CE 使用线性插值分别对齐具有不同步长的直方图。
传统的计算通常会产生更准确的结果,但被认为不太一致。
新的行为是设计使然。可以使用未记录且不受支持的跟踪标志 9474 来禁用它。这不是建议。您应该找到更好的方法来解决有问题的估计,例如使用临时表等。