可选参数,“索引查找”计划

时间:2011-08-18 08:39:20

标签: sql sql-server optional-parameters sql-execution-plan

在我的SELECT语句中,我使用可选参数:

DECLARE @p1 INT = 1  
DECLARE @p2 INT = 1  
SELECT name FROM some_table WHERE (id = @p1 OR @p1 IS NULL) AND (name = @p2 OR @p2 IS NULL)

在这种情况下,优化器为实体生成“索引扫描”(非搜索)操作,当参数提供非空值时,该操作不是最有效。 如果我将RECOMPILE提示添加到查询中,优化器会构建使用“seek”的更有效的计划。它适用于我的MSSQL 2008 R2 SP1服务器,它还意味着优化器CAN构建了一个只考虑我查询的一个逻辑分支的计划。
我怎样才能在任何地方使用该计划而无需重新编译? USE PLAN暗示在这种情况下不起作用。

以下是测试代码:

-- see plans  
CREATE TABLE test_table(  
    id INT IDENTITY(1,1) NOT NULL,   
    name varchar(10),    
    CONSTRAINT [pk_test_table] PRIMARY KEY CLUSTERED (id ASC))  
GO  
INSERT INTO test_table(name) VALUES ('a'),('b'),('c')  
GO  
DECLARE @p INT = 1  
SELECT name FROM test_table WHERE id = @p OR @p IS NULL  
SELECT name FROM test_table WHERE id = @p OR @p IS NULL OPTION(RECOMPILE)  
GO  
DROP TABLE test_table  
GO  

请注意,并非所有版本的SQL Server都会以我显示的方式更改计划。

4 个答案:

答案 0 :(得分:1)

您获得扫描的原因是谓词不会短路,并且始终会评估两个语句。正如您已经声明的那样,它不适用于优化器并强制扫描。即使with recompile有时似乎有所帮助,但它并不一致。

如果你有一张大型桌子,那么你必须有两个选择:

  1. 动态sql。
  2. 如果语句将您的查询分开并因此创建单独的执行计划(当@p is null时,您当然会总是进行扫描)。

答案 1 :(得分:1)

对Andreas'答案的评论的回复

问题是你需要两个不同的计划。

  • 如果@p1 = 1,那么您可以在索引上使用SEEK。
  • 如果@p1 IS NULL,它不是搜索,根据定义,它是扫描。

这意味着当优化器生成计划 Prior 以了解参数时,需要创建一个可以满足所有可能性的计划。只有扫描才能满足两者 @p1 = 1 @p1 IS NULL的需求。

这也意味着如果在参数已知且@p1 = 1时重新编译计划,则可以创建SEEK计划

正如您在评论中提到的那样,IF语句可以解决您的问题;每个IF块代表问题空间的不同部分,每个都可以给出不同的执行计划。

答案 2 :(得分:1)

请参阅Dynamic Search Conditions in T-SQL

这全面解释了RECOMPILE选项的工作原理以及不适用的替代版本。

答案 3 :(得分:0)

看看这篇文章http://www.bigresource.com/Tracker/Track-ms_sql-fTP7dh01/ 您似乎可以尝试使用提案解决方案:

`SELECT * FROM <table> WHERE IsNull(column, -1) = IsNull(@value, -1)`

`SELECT * FROM <table> WHERE COALESCE(column, -1) = COALESCE(@value, -1)`