不知道从哪里开始 - 不确定问题是我是在欺骗查询优化器,还是在涉及空值时索引的工作方式是内在的。
我遵循的一个编码约定是编写类似的存储过程:
declare procedure SomeProc
@ID int = null
as
select
st.ID,st.Col1,st.Col2
from
SomeTable st
where
(st.ID = @ID or @ID is null) --works, but very slow (relatively)
当然,在这个简单的测试用例中不是很有用,但是当你希望存储过程对整个表或符合某些条件的行进行操作时,在其他情况下很有用。但是,当在更大的表上使用时,这个速度相当慢...比用where替换where子句大约慢3-5倍:
where
st.ID = @ID --3-5x faster than first example
我更加困惑的是,用-1替换null给我几乎与上面“修复”WHERE子句相同的速度:
declare procedure SomeProc
@ID int = -1
as
select
st.ID,st.Col1,st.Col2
from
SomeTable st
where
(st.ID = @ID or @ID=-1) --much better... but why?
显然,这是一个空洞,使得事情变得古怪,但为什么呢?通过检查执行计划,我不清楚答案。这是我多年来在SQL Server的各种数据库,表格和版本上注意到的,所以我认为这不是我当前环境的怪癖。 我通过将默认参数值从null切换为-1来解决了这个问题。我的问题是为什么这是有效的。
注释
答案 0 :(得分:6)
你有很多问题,很可能是
但是没有看到计划,这些都是有根据的猜测。
参数嗅探
...默认为“NULL”。尝试使用不同的默认值,例如-1或没有默认值。
@ID = -1,默认值为NULL,参数sniffing =普通检查,所以速度更快。
您还可以在SQL Server 2008中尝试OPTIMISE FOR UNKNOWN
OR运算符
一些想法..
如果列不可为空,则在大多数情况下,优化程序会忽略条件
st.ID = ISNULL(@ID, st.ID)
此外,您可以使用IF语句
IF @ID IS NULL
SELECT ... FROM...
ELSE
SELECT ... FROM... WHERE st.ID
或UNION ALL以类似的方式。
就个人而言,我在大多数情况下都会使用参数屏蔽(总是)和ISNULL(我先试试)
alter procedure SomeProc
@ID int = NULL
AS
declare @maskID int
select @maskID = @ID
...