Sql server Sql Server如何为存储过程中的逻辑流编译执行计划?

Sql server Sql Server如何为存储过程中的逻辑流编译执行计划?,sql-server,Sql Server,我需要使用同一个查询两次,但where子句略有不同。我想知道如果用一个位值简单地调用同一个存储过程,并有一个if。。。其他的语句,决定要比较的字段 或者我应该创建两个存储过程,并根据应用程序中的逻辑调用每个过程 我想更详细地了解这一点,以便正确理解。 如何为此编制执行计划?是否每个IF中的每个代码块都有一个。。。还有 还是将其编译为一个大的执行计划?它使用传递到过程中的参数的初始值编译一次。尽管有些语句可能需要延迟编译,在这种情况下,最终编译时,它们将使用任何参数值进行编译 您可以从下面的运行和

我需要使用同一个查询两次,但where子句略有不同。我想知道如果用一个位值简单地调用同一个存储过程,并有一个if。。。其他的语句,决定要比较的字段

或者我应该创建两个存储过程,并根据应用程序中的逻辑调用每个过程

我想更详细地了解这一点,以便正确理解。 如何为此编制执行计划?是否每个IF中的每个代码块都有一个。。。还有


还是将其编译为一个大的执行计划?

它使用传递到过程中的参数的初始值编译一次。尽管有些语句可能需要延迟编译,在这种情况下,最终编译时,它们将使用任何参数值进行编译

您可以从下面的运行和查看实际执行计划中看到这一点

CREATE TABLE T
  (
     C INT
  )

INSERT INTO T
SELECT 1 AS C
UNION ALL
SELECT TOP (1000) 2
FROM   master..spt_values
UNION ALL
SELECT TOP (1000) 3
FROM   master..spt_values

GO

CREATE PROC P @C INT
AS
    IF @C = 1
      BEGIN
          SELECT '1'
          FROM   T
          WHERE  C = @C
      END
    ELSE IF @C = 2
      BEGIN
          SELECT '2'
          FROM   T
          WHERE  C = @C
      END
    ELSE IF @C = 3
      BEGIN
          CREATE TABLE #T
            (
               X INT
            )

          INSERT INTO #T
          VALUES     (1)

          SELECT '3'
          FROM   T,
                 #T
          WHERE  C = @C
      END

GO

EXEC P 1

EXEC P 2

EXEC P 3

DROP PROC P

DROP TABLE T 

运行
2
案例将来自
T
的估计行数显示为
1
而不是
1000
,因为该语句是根据
1
传入的初始参数值编译的。运行
3
案例给出了
1000
的准确估计计数,因为对(尚未创建)临时表的引用意味着该语句受到延迟编译的影响。

您应该关心正在缓存的执行计划

Martin给出了一个很好的例子,说明计划是缓存的,并且在第一次执行时将针对逻辑的某个分支进行优化。 在第一次执行之后,即使您使用不同的参数调用存储过程(存储过程),导致您的执行流选择另一个分支,也会重用该计划。 这非常糟糕,会影响性能。我见过这种情况很多次,要找到根本原因需要一段时间

这背后的原因被称为“参数嗅探”,它非常值得研究

一个常见的建议解决方案(我不建议这样做)是将您的存储过程分成几个小部分。 如果在存储过程中调用存储过程,则该内部存储过程将获得针对传递给它的参数优化的执行计划

在没有充分理由(模块化是一个很好的理由)的情况下,将一个存储过程拆分为几个较小的存储过程是一个丑陋的解决方法。Martin表明,通过对模式进行更改,可以重新编译语句。 我会在语句末尾使用选项(重新编译)。这将指示优化器在考虑所有变量的当前值的情况下重新编译语句:不仅考虑参数,还考虑局部变量,这会区分好的计划和坏的计划

回到您的问题,即根据参数使用不同的where子句构造查询。我将使用以下模式:

WHERE
(@parameter1 is null or col1 = @parameter1  ) 
AND
(@parameter2 is null or col2 = @parameter2  ) 
...
OPTION (RECOMPILE)
不利的一面是,该语句的执行计划从未被缓存(但它不会影响到语句的缓存),如果存储过程被多次执行,这可能会产生影响,因为现在应该考虑编译时间。使用生产质量数据执行测试将为您提供问题与否的答案

这样做的好处是,您可以编写可读且优雅的存储过程,而不会让优化器出错

要记住的另一个选项是,您可以在存储过程级别(与语句级别相反)禁用执行计划缓存,该级别粒度较小,更重要的是,在优化时不会考虑局部变量的值

更多信息请访问
http://sqlinthewild.co.za/index.php/2009/03/19/catch-all-queries/

如果……否则……(或
案例
)具体包含什么?在某些情况下,将条件抽象为参数可能更有效。每个进程有一个执行计划,因此条件逻辑可能会导致性能不均衡。