admin

SQL Server DateTime 参数“四舍五入”警告

sql

更多的是警告而不是问题:

我们今天早上解决了一个非常令人费解的错误。我们有多种报告,允许用户输入他们想要运行的日期范围。假设是,如果您要求从 2010 年 8 月1 日到 2010 年 8 月 10 日的报告,您打算包括 2010 年8 月 10 日,因此报告的结束日期不是 8 月 10 日,而是在那之后.

不可能是 2010 年 11 月 8 日,因为其中一些报告汇总了一天中发生的所有事情,将它们按当天午夜分组,因此每日汇总将包括额外的一天 - 不是我们想要的。

为了避免在非常接近一天结束时丢失任何项目的可能性,我们将结束日期计算为比明天少“一个刻度”:

public static DateTime EndOfDay(DateTime day)
{
    return day.Date.AddDays(1).AddTicks(-1);
}

在内部,这最终类似于 8/10/2010 12:59:59.9999PM

好吧,当您将此 DateTime 传递给 SQL Server 中的 DATETIME 参数时,它会将值向上舍入到 8/11/2010 00:00:00!由于我们的查询使用

DateField BETWEEN @FromDate AND @ToDate

代替

DateField >= @FromDate AND DateField < @ToDate

我们看到 8/1/2010-8/10/2010 的报告包括 8/11/2010 的项目。

我们发现真正问题的唯一方法是通过字符串来回转换日期。DateTime.ToString() 也是四舍五入,所以我们最终会得到 SQL Server 满意的 8/1/2010 12:59:59PM。

所以现在我们的“一天结束”方法看起来像这样:

public static DateTime EndOfDay(DateTime day)
{
    // Cant' subtract anything smaller (like a tick) because SQL Server rounds UP! Nice, eh?
    return day.Date.AddDays(1).AddSeconds(-1);
}

抱歉不是问题 - 只是认为有人可能会觉得它有用。


阅读 242

收藏
2021-07-01

共1个答案

admin

这是因为 DATETIME 数据类型的准确性,它具有准确性 ( quote):

舍入到 .000、.003 或 0.007 秒的增量

所以是的,在某些情况下您必须小心(例如,23:59:59.999 将向上舍入到第二天的 00:00,23:59:59.998 将向下舍入到 23:59:59.997)

SELECT CAST('2010-08-27T23:59:59.997' AS DATETIME)
SELECT CAST('2010-08-27T23:59:59.998' AS DATETIME)
SELECT CAST('2010-08-27T23:59:59.999' AS DATETIME)

从 SQL Server 2008 开始,有一种新的DATETIME2数据类型,可提供低至 100 纳秒的更高准确度。

当我对包含时间元素的 DATETIME 字段进行查询时,出于这个原因,我不使用 BETWEEN。

例如我更喜欢

WHERE DateField >= '2010-08-27' AND DateField < '2010-08-28'

代替:

WHERE DateField BETWEEN '2010-08-27' AND '2010-08-27T23:59:59.997'
2021-07-01