检查日期范围的最快方法

Posted

技术标签:

【中文标题】检查日期范围的最快方法【英文标题】:Fastest way to check date range 【发布时间】:2009-05-21 15:20:53 【问题描述】:

我将事件存储在 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

谢谢,

【问题讨论】:

与其玩弄在午夜前的最后一刻(复杂,因为 MS SQL Server 只能精确到某个点,这也使得此代码难以转移到另一个 RDBMS),为什么不您只需使用 EventDate 每当您想处理这样的日期范围桶时,最好在一个边界上使用包含条件,在另一个边界上使用排除条件。 IE。说 【参考方案1】:

您始终可以使用 WHERE EventDate BETWEEN @DateStart AND @DateEnd 的替代语法

【讨论】:

请记住,BETWEEN 是包容性的。 正确,大致相当于一个=【参考方案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 THEN SET @DateEnd = @DateStart)

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

【讨论】:

你不会索引 DateCol,而不是一些涉及 DateCol 的公式吗?【参考方案3】:

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

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

【讨论】:

【参考方案4】:

试试这个:

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

编辑解决 Tom H 的评论: 我从来没有在日期字段上使用过索引。对我来说总是更好的是额外的整数列来处理年份和年份值并为它们编制索引。

【讨论】:

就“最快”而言,这种方法并不好,因为它排除了在 EventDate 上使用任何索引。【参考方案5】:

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

您应该考虑在表中添加一个名为 searchdate 或类似名称的列,以包含不包含时间的日期。我还建议将此列编入索引,尤其是当您要针对该列的数据进行大量搜索时。

当您查询此列时,您的 SQL 中不会有任何标量函数来剥夺索引的性能。

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

SQL Server 2008 对仅日期数据类型有更好的支持。你也可以去看看。

【讨论】:

【参考方案6】:

我认为这个 T-SQL 相当于你的代码:

-- 将@DateStart 的时间部分设置回午夜 SET @DateStart = CONVERT(DATETIME,CONVERT(VARCHAR(10),@DateStart,20),20) -- 将@DateEnd 的时间部分提前到下一个午夜之前的最后一刻 SET @DateEnd = CONVERT(DATETIME,CONVERT(VARCHAR(11),@DateEnd,20)+'23:59:59.997',21)

CONVERT 函数将处理 NULL,因此无需单独测试 NULL 值(当然,除非您正在执行一些特殊处理而不是显示的内容,并且没有传递 NULL 值通过查询谓词(即 WHERE 子句)。或者,也许您期望很多参数为 NULL,并且您希望避免调用 CONVERT 的开销。

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

-- 将@DateEnd 提前到第二天的午夜 SET @DateEnd = DATEADD(day,1,CONVERT(DATETIME,CONVERT(VARCHAR(10),@DateEnd,20),20))

并更改谓词以进行这样的范围测试:

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

您可以避免单独的 SET 语句,并将表达式直接移动到查询中,但我不认为这会提高性能,并使 SQL 语句更难阅读,您肯定希望保留 cmets ...

WHERE (EventDate >= CONVERT(DATETIME,CONVERT(VARCHAR(10),@DateStart,20),20) 和事件日期

【讨论】:

以上是关于检查日期范围的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

x86-64 上检查指针范围是不是跨越 N 字节对齐地址的最快方法?

Python - 在给定的大数范围内找到所有完美正方形的最快方法

检查日期范围与其他一些日期范围的缺失天数

MySQL 检查日期范围是不是在日期范围内

mysql检查其他日期范围内的日期范围

如何检查日期范围选择器中的日期是不是更改?