在其中使用OR子句时将使用索引

时间:2010-07-13 03:30:43

标签: sql sql-server tsql indexing

我用可选参数编​​写了一个存储过程。

 CREATE PROCEDURE dbo.GetActiveEmployee
   @startTime DATETIME=NULL,
   @endTime   DATETIME=NULL
 AS
   SET NOCOUNT ON

   SELECT columns
   FROM table
   WHERE (@startTime is NULL or table.StartTime >= @startTime) AND
         (@endTIme is NULL or table.EndTime <= @endTime)

我想知道是否会使用StartTime和EndTime上的索引?

6 个答案:

答案 0 :(得分:4)

是的,他们将被使用(很可能,检查执行计划 - 但我知道你的参数的可选性不应该有任何区别)

如果您的查询出现性能问题,则可能是参数嗅探的结果。尝试以下存储过程的变体,看看它是否有所不同:

CREATE PROCEDURE dbo.GetActiveEmployee
    @startTime DATETIME=NULL,
    @endTime   DATETIME=NULL
AS
    SET NOCOUNT ON

    DECLARE @startTimeCopy DATETIME
    DECLARE @endTimeCopy DATETIME
    set @startTimeCopy = @startTime
    set @endTimeCopy = @endTime

    SELECT columns
    FROM table
    WHERE (@startTimeCopy is NULL or table.StartTime >= @startTimeCopy) AND
         (@endTimeCopy is NULL or table.EndTime <= @endTimeCopy)

这会禁用参数嗅探(SQL服务器使用传递给SP的实际值来优化它) - 过去我已经解决了一些奇怪的性能问题 - 我仍然无法令人满意地解释原因。

您可能想要尝试的另一件事是根据参数的NULL值将查询拆分为多个不同的语句:

IF @startTime is NULL
BEGIN
    IF @endTime IS NULL
        SELECT columns FROM table
    ELSE
        SELECT columns FROM table WHERE table.EndTime <= @endTime
END    
ELSE
    IF @endTime IS NULL
        SELECT columns FROM table WHERE table.StartTime >= @startTime
    ELSE
        SELECT columns FROM table WHERE table.StartTime >= @startTime AND table.EndTime <= @endTime
BEGIN

这很麻烦,但是如果遇到问题可能值得一试 - 它之所以有帮助,是因为SQL服务器每个sql语句只能有一个执行计划,但是你的语句可能会返回截然不同的结果集。 / p>

例如,如果传入NULL和NULL,则将返回整个表和最佳执行计划,但是如果传入一小段日期,则行查找更有可能是最佳执行计划。

将此查询作为单个语句,SQL服务器必须在这两个选项之间进行选择,因此在某些情况下查询计划可能不是最佳的。通过将查询拆分为多个语句,SQL Server可以在每种情况下都有不同的执行计划。

(如果您愿意,也可以使用exec函数/动态SQL来实现相同的目标)

答案 1 :(得分:2)

有一篇很棒的文章与SQL中的动态搜索条件有关。我个人在文章中使用的方法是X = @X或@X IS NULL样式,最后添加了OPTION(RECOMPILE)。如果您阅读该文章,它将解释原因

http://www.sommarskog.se/dyn-search-2008.html

答案 2 :(得分:1)

是的,根据查询提供的StartTimeEndTime列上的索引可以使用。

但是,[variable] IS NULL OR...使查询无法进行查询。如果您不想使用IF语句(因为CASE是一个表达式,并且不能用于控制流决策逻辑),动态SQL是性能SQL的下一个替代方法。

IF @startTime IS NOT NULL AND @endTime IS NOT NULL
BEGIN

  SELECT columns
    FROM TABLE 
   WHERE starttime >= @startTime
       AND endtime <= @endTime

END
ELSE IF @startTime IS NOT NULL
BEGIN

  SELECT columns
    FROM TABLE 
   WHERE endtime <= @endTime

END    
ELSE IF @endTIme IS NOT NULL
BEGIN

  SELECT columns
    FROM TABLE
   WHERE starttime >= @startTime

END
ELSE
BEGIN

  SELECT columns
     FROM TABLE

END

答案 3 :(得分:1)

基于给定参数动态更改搜索是一个复杂的主题,并且以一种方式对其进行操作,即使只有非常小的差异,也会产生巨大的性能影响。关键是使用索引,忽略紧凑代码,忽略担心重复代码,必须做出好的查询执行计划(使用索引)。

阅读本文并考虑所有方法。您最好的方法取决于您的参数,数据,架构和实际使用情况:

Dynamic Search Conditions in T-SQL by by Erland Sommarskog

The Curse and Blessings of Dynamic SQL by Erland Sommarskog

上述文章中适用于此查询的部分是Umachandar's Bag of Tricks,但它基本上将参数默认为某个值以消除需要使用OR。这将提供最佳的索引使用和整体性能:

CREATE PROCEDURE dbo.GetActiveEmployee
    @startTime DATETIME=NULL,
    @endTime   DATETIME=NULL
AS
    SET NOCOUNT ON

    DECLARE @startTimeCopy DATETIME
    DECLARE @endTimeCopy DATETIME
    set @startTimeCopy = COALESCE(@startTime,'01/01/1753')
    set @endTimeCopy = COALESCE(@endTime,'12/31/9999')

    SELECT columns
    FROM table
    WHERE table.StartTime >= @startTimeCopy AND table.EndTime <= @endTimeCopy)

答案 4 :(得分:0)

可能不是。看一下Tony Rogerson SQL Server MVP的博客文章:

http://sqlblogcasts.com/blogs/tonyrogerson/archive/2006/05/17/444.aspx

您至少应该知道需要使用可靠的数据进行测试并检查执行计划。

答案 5 :(得分:-1)

我认为您无法保证将使用该索引。它将在很大程度上取决于表的大小,显示的列,索引的结构和其他因素。

您最好的选择是使用SQL Server Management Studio(SSMS)并运行查询,并包含“实际执行计划”。然后你可以研究它,看看究竟使用了哪个指数。

你会经常对你所发现的东西感到惊讶。

如果查询中有ORIN,则情况尤其如此。