Mar*_*ith 12 sql-server optimization database-internals
此连接项中的示例代码
显示一个错误
SELECT COUNT(*)
FROM dbo.my_splitter_1('2') L1
INNER JOIN dbo.my_splitter_1('') L2
ON L1.csv_item = L2.csv_item
Run Code Online (Sandbox Code Playgroud)
返回正确的结果。但以下返回不正确的结果(2014 年使用新的 Cardinality Estimator)
SELECT
(SELECT COUNT(*)
FROM dbo.my_splitter_1('2') L1
INNER JOIN dbo.my_splitter_1('') L2
ON L1.csv_item = L2.csv_item)
Run Code Online (Sandbox Code Playgroud)
因为它错误地将 L2 的结果加载到公共子表达式假脱机中,然后重播 L1 结果的结果。
我很好奇为什么两个查询之间的行为不同。Trace Flag 8675 显示工作的进入search(0) - transaction processing,失败的进入search(1) - quick plan。
所以我假设额外转换规则的可用性是行为差异背后的原因(例如,禁用 BuildGbApply 或GenGbApplySimple似乎可以修复它)。
但是为什么对于这些非常相似的查询的两个计划会遇到不同的优化阶段?从我读过的内容来看search (0),至少需要三个表,而第一个示例中肯定不满足该条件。
每个阶段都有进入条件。“至少有三个表引用”是我们在给出简单示例时谈论的入门条件之一,但它不是唯一的。
一般只允许基本的join和union进入搜索0;标量子查询、半连接等阻止进入搜索 0。这个阶段确实适用于非常常见的 OLTP 类型的查询形状。探索不太常见的事物所需的规则并未启用。您的示例查询有一个标量子查询,因此无法输入。
它还取决于您如何计算表引用。我从来没有用函数深入研究过这个问题,但逻辑可能正在计算表值函数以及它们产生的表变量。它甚至可以计算函数本身内部的表引用——我不确定;尽管我确实知道功能只是全面的艰苦工作。
这个错误GenGbApplySimple是丑陋的。这种计划形状始终是可能的,但由于成本原因而被拒绝,直到更改为 100 行假定表变量基数。USE PLAN例如,可以通过提示在 2014 年之前的 CE 上强制执行有问题的计划形状。
您认为新的 Connect 项目与之前报告的问题相同,这是正确的。
举个例子,以下查询符合搜索 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;
Run Code Online (Sandbox Code Playgroud)
做一个小的改变来包含一个标量子查询意味着它直接进入搜索 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;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
159 次 |
| 最近记录: |