2011-08-18 26 views
2

在我的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服务器,这也意味着优化器可以构建一个只考虑我的查询的一个逻辑分支的计划。
我该如何使它能够在任何地方使用该计划,而不需要重新编译?在这种情况下,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版本将改变我的计划显示的方式。

回答

1

您得到扫描的原因是因为谓词不会短路,并且总是会评估两个语句。正如你已经指出的那样,优化器不能很好地工作并强制扫描。尽管with recompile有时似乎有所帮助,但并不一致。

如果你有一个大表,其中的目的是必须的,那么你有两种选择:

  1. 动态SQL。
  2. 如果语句分隔您的查询,并因此创建单独的执行计划(当@p is null你当然总是会得到一个扫描)。
+0

谢谢你的回复。是的,我知道其他解决方法与寻求 - 使用IFs,重构SQL等。但很遗憾,我不能提供查询与优化器只为这个查询建立的计划... – LINQ2Vodka

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)` 
+1

这是一个错误匹配的OP逻辑。你需要'WHERE column = IsNull(@value,column)',并且这会产生非常不同的属性。 – MatBailie

1

回应评论Andreas的答案

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

  • 如果@p1 = 1那么你可以在索引上使用SEEK。
  • 如果@p1 IS NULL,但它不是一个寻找,根据定义它是一个扫描。

这意味着,当优化器在生成计划之前到参数的知识,它需要创建一个可以fullfil所有可能性的计划。只有一个扫描可以覆盖的需求@p1 = 1@p1 IS NULL

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

这就是您在评论中提到的IF语句解决您的问题的原因;每个IF块表示问题空间的不同部分,并且每个IF块可以被赋予不同的执行计划。