为什么在连接谓词中引用变量会强制嵌套循环?

Mar*_*ith 16 performance sql-server optimization query-performance

最近遇到了这个问题,在网上找不到任何讨论。

下面的查询

DECLARE @S VARCHAR(1) = '';

WITH T
     AS (SELECT name + @S AS name2,
                *
         FROM   master..spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2;
Run Code Online (Sandbox Code Playgroud)

总是得到一个嵌套循环计划

在此处输入图片说明

尝试使用INNER HASH JOININNER MERGE JOIN提示强制问题会产生以下错误。

由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示且不使用 SET FORCEPLAN 的情况下重新提交查询。

我找到了一种允许使用散列或合并连接的解决方法 - 将变量包装在聚合中。生成的计划成本显着降低(19.2025 与 0.261987)

DECLARE @S2 VARCHAR(1) = '';

WITH T
     AS (SELECT name + (SELECT MAX(@S2)) AS name2,
                *
         FROM   spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2; 
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

这种行为的原因是什么?有没有比我找到的更好的解决方法?(这可能不需要额外的执行计划分支)

Tom*_*m V 13

我已经在 SQL 2012 实例上尝试过您的查询,并且跟踪标志 4199 似乎解决了这个问题。启用它后,我获得了一个合并连接,总成本为 0.24,并且没有任何额外的分支。

此问题的特定知识库文章是当查询中的联接谓词在 SQL Server 2005 或 SQL Server 2008 中具有外部引用列时会出现性能问题

在此处输入图片说明

为了进一步符合条件,TF 4199 启用了所有优化器修复。有关更多信息,请参阅此链接。一次启用所有内容可能会产生奇怪的副作用,因此如果您能找到特定的修复程序,最好单独启用该修复程序。

您可以使用OPTION (QUERYTRACEON 4199);在每个查询的基础上启用跟踪标志;