为什么这些类似的查询使用不同的优化阶段(事务处理与快速计划)?


12

此连接项中的示例代码

显示一个错误

SELECT COUNT(*)
FROM   dbo.my_splitter_1('2') L1
       INNER JOIN dbo.my_splitter_1('') L2
         ON L1.csv_item = L2.csv_item

返回正确的结果。但是以下内容返回的结果不正确(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)

因为它错误地将L2的结果加载到公共子表达式假脱机中,然后重播L1结果的结果。

我对为什么两个查询之间的行为差​​异感到好奇。跟踪标志8675显示工作的一个进入search(0) - transaction processing而失败的一个进入search(1) - quick plan

因此,我认为其他转换规则的可用性是行为差异的背后原因(例如,禁用BuildGbApply或GenGbApplySimple似乎可以解决此问题)。

但是,为什么针对这些非常相似的查询的两个计划会遇到不同的优化阶段?根据我的阅读search (0),至少需要三个表,并且在第一个示例中肯定不满足该条件。

Answers:


7

每个阶段都有入学条件。当给出简单的示例时,“具有至少三个表引用”是我们讨论的进入条件之一,但这不是唯一的条件。

通常,只允许基本联接和联合输入搜索0;否则,不允许输入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;

进行少量更改以包含标量子查询意味着可以直接搜索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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.