SQL Server查询优化:Where(Col = @ Col或@ Col = Null)

时间:2009-12-20 19:21:38

标签: sql-server tsql

不知道从哪里开始 - 不确定问题是我是在欺骗查询优化器,还是在涉及空值时索引的工作方式是内在的。

我遵循的一个编码约定是编写类似的存储过程:

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来解决了这个问题。我的问题是为什么这是有效的。

注释

  1. SomeTable.ID已编入索引
  2. 它可能与(或可能实际上是)参数嗅探问题有关 Parameter Sniffing (or Spoofing) in SQL Server 无论如何,我一直都是 几乎完全用于测试 每次之后都会“执行SomeProc” 编辑/重新编译proc,即with 省略了可选参数。

1 个答案:

答案 0 :(得分:6)

你有很多问题,很可能是

  1. 参数嗅探
  2. OR不适合使用
  3. 但是没有看到计划,这些都是有根据的猜测。

    参数嗅探

    ...默认为“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
    ...