在Where子句中使用变量时增加过程的执行持续时间

时间:2013-09-15 07:52:22

标签: sql sql-server performance variables stored-procedures

我在SQL Server 2008 R2中执行了一个过程,脚本是:

DECLARE @LocalVar SMALLINT = GetLocalVarFunction();

SELECT 
  [TT].[ID],
  [TT].[Title]
FROM [TargetTable] AS [TT]
LEFT JOIN [AcceccTable] AS [AT] ON [AT].[AccessID] = [TT].[ID]
WHERE 
(
  (@LocalVar = 1 AND ([AT].[Access] = 0 OR [AT].[Access] Is Null) AND
  ([TT].[Level] > 7)
);
GO

此程序在16秒内执行。 但是,当我将Where子句更改为:

  WHERE 
(
  ((1=1) AND [AT].[Access] = 0 OR [AT].[Access] Is Null) AND
  ([TT].[Level] > 7)
);

程序执行时间不到1秒。

如您所见,我只是删除了局部变量。

问题出在哪里?在where子句中使用局部变量是否有任何遗漏?当我在where子句中使用局部变量时,有什么建议可以改善执行时间吗?

更新

我还想在脚本之前添加if语句并将过程拆分为2个过程,但是我有4或5个变量,如上所述并使用if语句是如此复杂。

UPDATE2:

我更改了@LocalVar

的设置
DECLARE @LocalVar SMALLINT = 1;

执行时间没有变化。

3 个答案:

答案 0 :(得分:2)

在WHERE中使用局部变量时,优化器不知道如何处理它。

您可以查看此link

在您的情况下,您可以执行的操作是在两种情况下都显示实际计划,并查看SQL如何处理它们。

答案 1 :(得分:2)

当您在WHERE过滤器中使用局部变量时,它会导致 FULL TABLE SCAN 。 SQL Server在编译时不知道局部变量的值。因此,SQL Server会为该列提供最大规模的执行计划。

正如您所看到的,当您通过1==1时,SQL服务器知道该值,因此性能不会降低。但是,当你传递局部变量时,该值是未知的。

一种解决方案可能是在SQL查询结束时使用 OPTION(RECOMPILE)

您可以查看OPTIMIZE FOR UNKNOWN

答案 2 :(得分:0)

您似乎正在使用@LocalVar作为分支条件,如下所示:

  • 如果@LocalVar为1,则将过滤器应用于查询
  • 如果@LocalVar为0,则返回空结果集。

IMO你最好明确地写这个条件,因为那时SQL可以优化2个分支的单独计划,即

DECLARE @LocalVar SMALLINT = GetLocalVarFunction();

IF (@LocalVar = 1)
    SELECT 
      [TT].[ID],
      [TT].[Title]
    FROM [TargetTable] AS [TT]
    LEFT JOIN [AcceccTable] AS [AT] ON [AT].[AccessID] = [TT].[ID]
    WHERE 
    (
      ([AT].[Access] = 0 OR [AT].[Access] Is Null) AND
      ([TT].[Level] > 7)
    )
ELSE
    SELECT 
      [TT].[ID],
      [TT].[Title]
    FROM [TargetTable] AS [TT]
    WHERE 1=2 -- Or any invalid filter, to retain the empty result

然后,因为现在存储过程中有2个分支,所以应该将WITH RECOMPILE添加到存储过程中,因为2个分支的查询计划完全不同。

修改

只是为了澄清评论:

请注意,在查询后放置OPTION(RECOMPILE)意味着query plan is never cached - 如果经常调用您的查询,这可能不是一个好主意。

PROC级别的WITH RECOMPILE可防止通过proc缓存分支。 与查询级别的OPTION(RECOMPILE)相同。

如果您的查询中有大量的过滤器排列,那么上面的“分支”技术就无法很好地扩展 - 您的代码很快就会变得无法维护。

然而,您可能需要考虑使用parameterized dynamic SQL。然后,SQL将至少为每个排列缓存一个单独的计划。