此连接项中的示例代码
显示一个错误
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 结果的结果。
我很好奇为什么两个查询之间的行为会有所不同。Trace Flag 8675 显示有效的进入search(0) - transaction processing
,失败的进入search(1) - quick plan
。
所以我假设额外转换规则的可用性是行为差异的背后原因(例如,禁用 BuildGbApply 或GenGbApplySimple似乎可以修复它)。
但是为什么针对这些非常相似的查询的两个计划会遇到不同的优化阶段呢?从我读过的内容来看search (0)
,至少需要三个表,而第一个示例中肯定不满足该条件。
每个阶段都有进入条件。“至少有三个表引用”是我们在举简单例子时所说的进入条件之一,但不是唯一的。
一般只允许基本的join 和union 进入search 0;标量子查询、半连接等阻止搜索 0 的条目。这个阶段实际上是针对非常常见的 OLTP 类型的查询形状。探索不常见事物所需的规则并未启用。您的示例查询有一个标量子查询,因此输入失败。
它还取决于您如何计算表引用。我从来没有用函数深入研究过这个,但逻辑可能正在计算表值函数以及它们产生的表变量。它甚至可以计算函数本身内部的表引用——我不确定;尽管我确实知道功能是全面的艰苦工作。
的错误
GenGbApplySimple
是丑陋的。这种计划形状始终是可能的,但由于成本原因而被拒绝,直到更改为 100 行假定表变量基数出现。USE PLAN
例如,可以通过提示在 2014 年之前的 CE 上强制使用有问题的计划形状。关于新的 Connect 项目与之前报告的问题相同,您是正确的。
举个例子,以下查询符合搜索 0 的条件:
做一个小的改变来包含一个标量子查询意味着它直接去搜索 1: