在我的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都会以我显示的方式更改计划。
答案 0 :(得分:1)
您获得扫描的原因是谓词不会短路,并且始终会评估两个语句。正如您已经声明的那样,它不适用于优化器并强制扫描。即使with recompile
有时似乎有所帮助,但它并不一致。
如果你有一张大型桌子,那么你必须有两个选择:
@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)`