lec*_*g92 21 sql-server datetime
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date <= '2015-07-27 23:59:59.999'
Run Code Online (Sandbox Code Playgroud)
但是结果中包含了今天posted_date 的记录:2015-07-28。我的数据库服务器不在我的国家。问题是什么 ?
Eri*_*rik 19
正如其他几个人在评论和其他问题的答案中提到的,核心问题2015-07-27 23:59:59.999
正在2015-07-28 00:00:00.000
由 SQL Server 解决。每文档为DATETIME:
时间范围 - 00:00:00 到 23:59:59.997
请注意,时间范围永远不会是.999
。在文档的进一步下方,它指定了 SQL Server 用于最低有效数字的舍入规则。
请注意,最低有效数字只能具有三个潜在值之一:“0”、“3”或“7”。
您可以使用多种解决方案/变通方法。
-- Option 1
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date < '2015-07-28 00:00:00.000' --Round up and remove equality
-- Option 2
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date <= '2015-07-27 23:59:59.997' --Round down and keep equality
-- Option 3
SELECT
*
FROM A
WHERE CAST(posted_date AS DATE) = '2015-07-27' -- Use different data type
-- Option 4
SELECT
*
FROM A
WHERE CONVERT(CHAR(8), DateColumn, 112) = '20150727' -- Cast to string stripping off time
-- Option 5
SELECT
*
FROM A
WHERE posted_date BETWEEN '2015-07-27 00:00:00.000'
AND '2015-07-27 23:59:59.997' --Use between
Run Code Online (Sandbox Code Playgroud)
在我上面介绍的五个选项中,我认为选项 1 和 3 是唯一可行的选项。它们清楚地传达了您的意图,并且在您更新数据类型时不会中断。如果您使用的是 SQL Server 2008 或更新版本,我认为选项 3 应该是您的首选方法。如果您可以从使用DATETIME数据类型更改DATE为posted_date
列的数据类型,则尤其如此。
关于选项 3,可以在此处找到有关某些问题的非常好的解释:Cast to date is sargable but it is a good idea?
我不喜欢选项 2 和 5,因为.997
小数秒将只是人们想要“修复”的另一个神奇数字。出于其他一些BETWEEN
未被广泛接受的原因,您可能需要查看这篇文章。
我不喜欢选项 4,因为为了比较目的将数据类型转换为字符串对我来说很脏。在 SQL Server 中避免使用它的一个更定性的原因是它会影响sargability,也就是您无法执行索引查找,这通常会导致性能下降。
有关正确的方式和错误的方式来处理日期范围查询的详细信息结帐这个职位由阿龙贝特朗。
在分离时,您将能够保留原始查询,并且如果您将posted_date
列从 a更改DATETIME为 a ,它将按预期运行DATETIME2(3)
。这将节省服务器上的存储空间,在相同精度下为您提供更高的精度,更符合标准/便携性,并允许您在将来的需求发生变化时轻松调整精度/精度。但是,如果您使用 SQL Server 2008 或更新版本,这只是一个选项。
根据StackOverflow 的答案1/300
,第二个准确度DATETIME似乎是 UNIX 的一个小问题。拥有共同遗产的Sybase1/300
在它们的数据类型DATETIME
和TIME
数据类型上具有相似的第二精度,但它们的最低有效数字在“0”、“3”和“6”处略有不同。在我看来1/300
,秒和/或 3.33 毫秒的精度是一个不幸的架构决策,因为SQL Server数据类型中的时间的4 字节块DATETIME可以轻松支持 1 毫秒的精度。
Kin*_*hah 16
由于您使用的是datetime数据类型,因此您需要了解 sql server 如何舍入日期时间数据。
??????????????????????????????????????????????????????????????????????????????????????????????????????
? Name ? sn ? Minimum value ? Maximum value ? Accuracy ? Storage ?
??????????????????????????????????????????????????????????????????????????????????????????????????????
? datetime ? dt ? 1753-01-01 00:00:00.000 ? 9999-12-31 23:59:59.997 ? 3.33 ms ? 8 bytes ?
? datetime2 ? dt2 ? 0001-01-01 00:00:00.0000000 ? 9999-12-31 23:59:59.9999999 ? 100ns ? 6-8 bytes ?
??????????????????????????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)
使用下面的查询,您可以很容易地看到使用DATETIME
数据类型时 sql server 所做的舍入问题。
select '2015-07-27 00:00:00.000' as Original_startDateTime,
convert(datetime ,'2015-07-27 00:00:00.000') as startDateTime,
'2015-07-27 23:59:59.999' as Original_endDateTime,
convert(datetime ,'2015-07-27 23:59:59.999') as endDateTime,
'2015-07-27 00:00:00.000' as Original_startDateTime2,
convert(datetime2 ,'2015-07-27 00:00:00.000') as startDateTime2, -- default precision is 7
'2015-07-27 23:59:59.999' as Original_endDateTime2,
convert(datetime2 ,'2015-07-27 23:59:59.999') as endDateTime2 -- default precision is 7
Run Code Online (Sandbox Code Playgroud)
DATETIME2
自 SQL Server 2008 以来一直存在,所以开始使用它而不是DATETIME
. 对于你的情况,你可以使用datetime2
与3位小数精度如datetime2(3)
。
使用的好处datetime2
:
datetime
只支持 3 个小数位……因此您会看到舍入问题,因为默认情况下,以,或秒为增量舍入datetime
最接近的。.003 seconds
.000
.003
.007
datetime2
比 更精确,datetime
并且可以datetime2
让您控制DATE
和TIME
与datetime
.参考 :
隐式转换
我认为posted_date 数据类型是Datetime。然而,另一端的类型是 Datetime、Datetime2 还是 Time 并不重要,因为字符串 (Varchar) 将被隐式转换为 Datetime。
将posted_date 声明为Datetime2(或Time),posted_date <= '2015-07-27 23:59:59.99999'
where 子句失败,因为虽然23:59:59.99999
是有效的Datetime2 值,但这不是有效的Datetime 值:
Conversion failed when converting date and/or time from character string.
Run Code Online (Sandbox Code Playgroud)
日期时间的时间范围
Datetime 的时间范围是 00:00:00 到 23:59:59.997。因此 23:59:59.999 超出范围,必须向上或向下舍入到最接近的值。
准确性
此外,日期时间值按 .000、.003 或 0.007 秒的增量四舍五入。(即 000, 003, 007, 010, 013, 017, 020, ..., 997)
2015-07-27 23:59:59.999
在此范围内的值不是这种情况:2015-07-27 23:59:59.997
和2015-07-28 0:00:00.000
。
此范围对应于最接近的前后选项,均以 .000、.003 或 .007 结尾。
向上或向下舍入?
因为它比 更接近2015-07-28 0:00:00.000
(+1 对 -2)2015-07-27 23:59:59.997
,所以字符串被四舍五入并成为这个日期时间值:2015-07-28 0:00:00.000
。
使用类似2015-07-27 23:59:59.998
(或 .995, .996, .997, .998) 的上限,它会被四舍五入到2015-07-27 23:59:59.997
并且您的查询会按预期工作。然而,它不会是一个解决方案,而只是一个幸运值。
Datetime2 或 Time 类型
DATETIME2和时间的时间范围是00:00:00.0000000
通过23:59:59.9999999
具有100ns的精度(具有7位精度使用时最后一位)。
但是 Datetime(3) 范围与 Datetime 范围不同:
0:0:00.000
到23:59:59.997
0:0:00.000000000
到23:59:59.999
解决方案
最后,查找低于或等于您认为是一天中最后一段时间的日期比查找低于或等于第二天的日期更安全。这主要是因为您知道第二天总是从 0:00:00.000 开始,但不同的数据类型在一天结束时可能不会有相同的时间:
Datetime `0:0:00.000` to `23:59:59.997`
Datetime2 `0:0:00.000000000` to `23:59:59.999-999-900`
Time2 `0:0:00.000000000` to `23:59:59.999-999-900`
Run Code Online (Sandbox Code Playgroud)
< 2015-07-28 0:00:00.000
会给你一个准确的结果,是最好的选择<= 2015-07-27 23:59:59.xxx
当它没有四舍五入到您认为应该是的值时,可能会返回意外的值。我们可以认为将 [posted_date] 更改为 Datetime2 及其更高的精度可以解决此问题,但这无济于事,因为字符串仍会转换为 Datetime。但是,如果添加演员表cast(2015-07-27 23:59:59.999' as datetime2)
,这可以正常工作
投射和转换
Cast 可以将最多 3 位的值转换为 Datetime 或最多 9 位的值转换为 Datetime2 或 Time 并将其四舍五入到正确的精度。
需要注意的是,Datetime2 和 Time2 的 Cast 可能会给出不同的结果:
select cast('20150101 23:59:59.999999999' as datetime2(7))
向上取整 2015-05-03 00:00:00.0000000(值大于 999999949)select cast('23:59:59.999999999' as time(7))
=> 23:59:59.9999999它解决了日期时间以 0、3 和 7 为增量的问题,尽管在第二天的 1 纳秒之前查找日期总是更好(总是 0:00:00.000)。
源 MSDN:日期时间 (Transact-SQL)
这是四舍五入
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
Run Code Online (Sandbox Code Playgroud)
.998, .997, .996, .995 全部施放/舍入到 .997
应该使用
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
Run Code Online (Sandbox Code Playgroud)
或者
where cast(posted_date as date) = '2015-07-27'
Run Code Online (Sandbox Code Playgroud)
请参阅此链接中的准确性
始终报告为 .000、.003、.007