1

我在 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语句非常复杂。

更新2:

我改变了一组@LocalVar

DECLARE @LocalVar SMALLINT = 1;

执行时间没有变化。

4

3 回答 3

2

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

您可以查看此链接

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

于 2013-09-15T07:58:22.890 回答
2

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

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

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

您可以查看OPTIMIZE FOR UNKNOWN

于 2013-09-15T08:02:58.040 回答
0

看来您正在使用@LocalVaras 分支条件,如下所示:

  • 如果@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)在查询之后意味着查询计划永远不会被缓存- 如果您的查询被频繁调用,这可能不是一个好主意。

WITH RECOMPILEPROC 级别防止通过 proc 缓存分支。它OPTION(RECOMPILE)查询级别不同。

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

不幸的是,您可能需要考虑使用参数化动态 SQL。然后,SQL 将至少为每个排列缓存一个单独的计划。

于 2013-09-15T08:10:22.143 回答