为什么 OPTIMIZE FOR UNKNOWN 将我的查询改进了几秒钟?

Der*_*sar 7 sql-server optimization execution-plan sql-server-2014 parameter-sniffing

好的,所以我有一个我们在 SSRS 报告中使用的非存储过程查询。这个查询非常慢(过去两个小时我已经运行了这个查询的原始版本,但仍未完成),为了改进它,我从头开始重写它,我想出了以下内容:

现在这里是无聊的单词问题部分:

我们希望为TOP 5每个销售代表提取一份客户列表,但从该列表中排除所有TOP 10客户。(因此,如果 John Doe 有客户 A、B、C、D 和 E,而客户 C 是前 10 名之一,则只提取 A、B、D 和 E。)

为此,第一个查询使用了IN (... NOT IN ( ) ),所以我认为是嵌套IN的问题,为了重写它,我做了一个OUTER APPLY真正打破一切的查询。

无论如何,我修复了所有这些并运行了查询,但仍然需要 10-15 秒,我认为这是参数嗅探。为了进行调查,我在 SSMS 中运行了查询,添加了OPTION (RECOMPILE)(查看会生成什么查询计划),并得到以下内容:

方案一

可以在 Brent Ozar 的“粘贴计划”中查看此处。生成这个的查询是:

DECLARE @Top10Temp TABLE (Id INT)
INSERT INTO @Top10Temp
SELECT TOP 10 Id
FROM Object1
WHERE Column2  = @ReportId
  AND Column3  = 0
GROUP BY Id
ORDER BY SUM(Column4 + Column5) DESC

SELECT Object2.*
FROM Object1 AS Object2
OUTER APPLY (
    SELECT TOP 5
        Object3.Id,
        SUM(Object3.Column4 + Object3.Column5) AS Column6
    FROM Object1 AS Object3
    WHERE Object3.Column3 = 0
      AND Object3.Column7 = Object2.Column7
      AND Object3.Column2 = @ReportId
    GROUP BY
        Object3.Id
    ORDER BY
        SUM(Object3.Column4 + Object3.Column5) DESC
) AS Object4
WHERE Object2.Column2 =      @ReportId
  AND Object2.Column3 =      0
  AND Object2.Id      =      Object4.Id
  AND Object2.Id      NOT IN (SELECT Id FROM @Top10Temp)
ORDER BY Object2.Column7
OPTION (RECOMPILE)
Run Code Online (Sandbox Code Playgroud)

现在是相同的查询,OPTION (OPTIMIZE FOR UNKNOWN)生成了以下计划:

方案二

也可以在“粘贴计划”中查看。该计划在不到 1 秒内执行完毕

如果我补充OPTION (OPTIMIZE FOR (@ReportId = #)),这里#相同@ReportId变量,我得到相同的查询计划,作为第二。

我做错什么了吗?我无法理解发生了什么,因此非常感谢您提供任何信息。(我也真的不喜欢试图通过提示影响优化器,但如果有必要,我会保留它。)

thu*_*con 5

“为了调查,我在 SSMS 中运行了查询......”这就是问题所在。局部变量使用统计的密度向量产生更好的行估计,因此已经优化了 UNKNOWN。参数化动态 SQL 使用直方图,它提取给定部分的整个行数。

查看每个粘贴计划链接的估计行数与实际行数。第二个链接比第一个链接有更好的估计。

我会将您的 SSRS 查询部署到开发实例并运行一些测试,因为我怀疑您可能遇到性能问题。

顺便说一句,如果可以的话,更新统计信息或重建这些野兽表上的索引。

链接: 在统计直方图和密度向量中