为什么这些相似的查询使用不同的优化阶段(事务处理与快速计划)?
-
29-09-2020 - |
题
示例代码 在此连接项中
显示错误所在
SELECT COUNT(*)
FROM dbo.my_splitter_1('2') L1
INNER JOIN dbo.my_splitter_1('') L2
ON L1.csv_item = L2.csv_item
返回正确的结果。但以下返回不正确的结果(2014 年使用新的基数估计器)
SELECT
(SELECT COUNT(*)
FROM dbo.my_splitter_1('2') L1
INNER JOIN dbo.my_splitter_1('') L2
ON L1.csv_item = L2.csv_item)
因为它错误地将 L2 的结果加载到公共子表达式假脱机中,然后重播 L1 结果的结果。
我很好奇为什么两个查询之间的行为存在差异。跟踪标志 8675 显示有效的进入 search(0) - transaction processing
失败者进入 search(1) - quick plan
.
因此,我假设额外转换规则的可用性是行为差异背后的原因(禁用 BuildGbApply 或 GenGbApplySimple 例如,似乎可以修复它)。
但是为什么这些非常相似的查询的两个计划会遇到不同的优化阶段呢?从我读到的 search (0)
需要至少三个表,而第一个示例中肯定不满足该条件。
解决方案
每个阶段都有进入条件。“至少有三个表引用”是我们在给出简单示例时谈论的进入条件之一,但它不是唯一的条件。
一般只允许基本的join和union进入search 0;标量子查询、半连接等。阻止进入搜索 0。此阶段实际上适用于非常常见的 OLTP 类型查询形状。探索不常见事物所需的规则尚未启用。您的示例查询有一个标量子查询,因此输入失败。
它还取决于您如何计算表引用。我从未深入研究过函数,但逻辑可能是对表值函数以及它们生成的表变量进行计数。它甚至可能正在计算函数本身内部的表引用 - 我不确定;尽管我确实知道函数只是一项艰苦的工作。
该错误与 GenGbApplySimple
很丑。这种计划形状始终是一种可能性,但由于成本原因而被拒绝,直到更改为 100 行假定表变量基数。可以使用以下命令将有问题的计划形状强加到 2014 年之前的 CE 上: USE PLAN
提示,例如。
举个例子,以下查询符合搜索 0:
DECLARE @T AS table (c1 integer NULL);
SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY U.c1)
FROM
(
SELECT c1 FROM @T AS T
UNION
SELECT c1 FROM @T AS T
UNION
SELECT c1 FROM @T AS T
) AS U;
进行一个小更改以包含标量子查询意味着它会直接搜索 1:
DECLARE @T AS table (c1 integer NULL);
SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -- Changed!
FROM
(
SELECT c1 FROM @T AS T
UNION
SELECT c1 FROM @T AS T
UNION
SELECT c1 FROM @T AS T
) AS U;