为什么 OPTION RECOMPILE 会导致谓词下推?

Rad*_*hiu 8 sql-server execution-plan azure-sql-database recompile query-performance

我有一个 SQL 查询,它是由一堆嵌套的视图和表值函数组成的,深度至少为 4 层(我没有时间或耐心去完成这一切,它有数百行代码在每个级别)。

我一直试图理解为什么当我使用选项(重新编译)运行基本查询时它运行得非常快,但是当我在没有这个选项的情况下运行它时,它运行得非常慢。

我已确保在发生这种情况之前清除计划缓存,即使在生成新计划时,它也不是最佳的,但是,选项(重新编译)速度很快。

我检查了这两个计划,并注意到对于带有选项(重新编译)的计划,传递的参数。

SELECT [p].[Activity]
    ,[p].[ActivityType]
    ,[p].[Company]
    ,[p].[Flags]
    ,[p].[Id]
    ,[p].[Name]
    ,[p].[Priority]
    ,[p].[Filters]
    ,[p].[Priority]
    ,[p].[Classification]
    ,[p].[Number]
    ,[p].[TaskFilter]
    ,[p].[TaskType]
    ,[p].[User]
FROM (
    SELECT *
    FROM [ActivProdStatuses]('ProdJobTask', 0)
    ) AS [p]
WHERE (
        (   ([p].[User] = 'some_value') AND (([p].[Flags] & 8) = 0) ) 
           AND ([p].[Activity] = 'unique_value') 
        )
    AND  
      (CASE 
        WHEN ([p].[Flags] & 4) <> 0
         THEN CAST(1 AS BIT)
         ELSE CAST(0 AS BIT)
       END = 1 )
ORDER BY [p].[Priority]
OPTION (RECOMPILE)
Run Code Online (Sandbox Code Playgroud)

在没有 OPTION RECOMPILE 的计划中,我的计划中有一部分时间花在移动无用数据上,这些数据稍后由 FILTER 运算符过滤(您可以看到 0B 从 FILTER 中出来)。

在此输入图像描述

unique_valueFILTER 运算符具有过程 (和)附带的所有过滤参数some_value以及在嵌套级别中确定的一些其他过滤参数。嵌套级别本身包含其他 TVF,其参数由 OUTER APPLY'ed 查询确定。

在此输入图像描述

在 OPTION RECOMPILE 版本中,当从磁盘读取数据时,这些参数被下推到执行计划(我猜这称为谓词下推)并直接在第一步中进行过滤。

根据我的观察,这是我对为什么会发生这种情况以及为什么 OPTION RECOMPILE 计划更有效的结论。您可以在下面看到同一个表的相同数据访问以及更高效的 OPTION RECOMPILE 计划的一部分。

在此输入图像描述


现在,我的问题是为什么 OPTION RECOMPILE 计划的行为与仅为新查询生成新计划(传递给它的参数/值相同)时的行为不同。选项重新编译有什么作用?

我尝试在线搜索,看看它是否确实“强制”谓词下推,但我找不到任何具体的信息。

您可以在此处找到执行缓慢的匿名计划。此外,OPTION RECOMPILE 执行匿名计划位于此处

我尝试使用 OPTION (RECOMPILE) 运行一次查询,然后删除提示并立即再次运行查询(两次参数相同)。第一次跑得快,第二次跑得慢。

我确实考虑过生成“快速”计划,希望将其存储在缓存中,并在不再指定 OPTION RECOMPILE 时重用。但是,我相信查询计划哈希有所不同,并且缓存的计划不会被重用,因为存在的更改不仅仅是传递到过滤子句的值。

Pau*_*ite 20

现在,我的问题是为什么 OPTION RECOMPILE 计划的行为与仅为新查询生成新计划(传递给它的参数/值相同)时的行为不同。选项重新编译有什么作用?

主要的事情OPTION (RECOMPILE)是为任何参数的当前值编译一个计划,而不是重用任何缓存的计划。新生成的计划不会被缓存以供重用。

它启用的第二件事是参数嵌入优化。SQL Server 在优化之前将任何参数替换为其文字值。

这听起来可能微不足道,但可以实现重要的简化。例如,可以尽早评估该值所属的任何类型转换或复杂表达式(常量折叠)。请注意屏幕截图中的 ,将提供的varcharCONVERT_IMPLICIT值转换为nvarchar。我注意到您的数据库已启用强制参数化。

您的计划很大并且是匿名的,但我建议参数嵌入及其带来的重大简化是性能显着提高的原因。

当您运行相同的查询时,如果没有OPTION (RECOMPILE)相同的简化是不可能的,因为 SQL Server 无法安全地嵌入参数值,因为计划可能会重复用于不同的值。

更多信息和背景请参阅我的文章(上面链接)参数嗅探、嵌入和重新编译选项。它包含一个工作示例,逐步展示参数嵌入和常量折叠如何改进执行计划。