检查日期范围的最快方法

时间:2009-05-21 15:20:54

标签: sql sql-server sql-server-2005 tsql

我在SQLServer 2005中存储事件,事件发生的时间很重要,必须存储在日期库中。在where子句中编写日期范围检查的最快方法是什么,以确保选择当天的所有内容?

目前,当传递@DateStart和@DateEnd时,我将@DateStart设置为午夜并将@DateEnd设置为午夜之前的最后一刻,这是当天捕捉每一个可能事件的第一件事。

IF (@DateStart IS NOT NULL)
BEGIN
    SET @DateStart = CAST   (
                                (   CAST (DATEPART (yyyy,@DateStart) AS NVARCHAR(4)) +'/'+
                                    CAST (DATEPART (mm,@DateStart) AS NVARCHAR(2)) +'/'+
                                    CAST (DATEPART (dd,@DateStart) AS NVARCHAR(2)) +' '+
                                    '00:00:00.000'
                                )
                            AS DATETIME)
END

IF (@DateEnd IS NOT NULL)
BEGIN
    SET @DateEnd = CAST (
                            (   CAST (DATEPART (yyyy,@DateEnd) AS NVARCHAR(4)) +'/'+
                                CAST (DATEPART (mm,@DateEnd) AS NVARCHAR(2)) +'/'+
                                CAST (DATEPART (dd,@DateEnd) AS NVARCHAR(2)) +' '+
                                '23:59:59.997'
                            )
                            AS DATETIME
                        )
END

所以where子句很容易阅读:

WHERE(EventDate> = @DateStart AND EventDate< = @DateEnd)

谢谢,

6 个答案:

答案 0 :(得分:7)

您始终可以使用WHERE EventDate BETWEEN @DateStart AND @DateEnd

的替代语法

答案 1 :(得分:2)

你的where子句看起来像;

WHERE DateCol >= DATEADD(dd, DATEDIFF(dd, 0, @DateStart), 0) --Midnight on the Start date
    AND DateCol < DATEADD(dd, DATEDIFF(dd, 0, @DateEnd + 1), 0) --Midnight of the day after End date

并且你的所有IF语句都会处理空参数(即IF @DateEnd IS NULL,然后设置@DateEnd = @DateStart)

如果您的表很大,您可能想要在DATEADD(dd,DATEDIFF(dd,0,DateCol),0)上建立索引。

答案 2 :(得分:1)

截断日期的最快方法,前一个午夜:

DATEADD(day,DATEDIFF(day,'19010101',LastModifiedDate),'19010101')

下午六点:

DATEADD(day,DATEDIFF(day,'19010101',LastModifiedDate)+1,'19010101')

您也可以将其包装为内联UDF:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/05/23/reuse-your-code-with-cross-apply.aspx

答案 3 :(得分:0)

AlexK可能是最好的主意。 我唯一担心的是在谓词中使用函数的性能。

您应该考虑在表中添加名为searchdate或类似名称的列,以包含没有时间的日期。我还建议将此列编入索引,特别是如果您要对该列的数据进行大量搜索。

当您在此列上查询时,SQL中将没有任何标量函数来抢夺索引的性能。

con ...好吧,额外的存储空间,插入期间写入数据的时间(虽然这里不多)。

SQL Server 2008更好地支持仅限日期的数据类型。你也可以办理登机手续。

答案 4 :(得分:0)

我认为这个T-SQL等同于你拥有的代码:

    -- set time portion of @DateStart back to midnight
    SET @DateStart = CONVERT(DATETIME,CONVERT(VARCHAR(10),@DateStart,20),20)

    -- advance time portion of @DateEnd to last instant before next midnight
    SET @DateEnd = CONVERT(DATETIME,CONVERT(VARCHAR(11),@DateEnd,20)+'23:59:59.997',21)

CONVERT函数将处理NULLS,因此不需要对NULL值进行单独测试(当然,除非您正在显示的内容之外进行一些特殊处理,否则不会通过NULL值到查询谓词(即WHERE子句)。或者,您可能希望很多参数为NULL,并且您希望避免调用CONVERT的开销。

但是,我同意Tom H.的建议,并且避免弄乱减去毫秒,而是将@DateEnd设置为第二天的午夜,例如

    -- advance @DateEnd to midnight of following day
    SET @DateEnd = DATEADD(day,1,CONVERT(DATETIME,CONVERT(VARCHAR(10),@DateEnd,20),20))

并更改谓词以执行范围测试,如下所示:

WHERE (EventDate >= @DateStart AND EventDate < @DateEnd)

你可以避免使用单独的SET语句,并将表达式直接移到查询中,但我不希望这会提高性能,并使SQL语句更难读,你肯定想保留这些注释...

    WHERE(EventDate&gt; = CONVERT(DATETIME,CONVERT(VARCHAR(10),@ DateStart,20),20)        和EventDate

答案 5 :(得分:0)

试试这个:

WHERE DATEPART(yyyy, EventDate) = DATEPART(yyyy, getdate()) 
      AND DATEPART(dy, EventDate) = DATEPART(dy, getdate())  --day of year

编辑解决Tom H的评论: 我对日期字段中的索引没有任何好运;对我来说总是更好的是额外的整数列来处理年份和年份的值,并将其编入索引。