使用GETDATE()-n查询datetime2(7)字段时匹配值被排除的问题
GETDATE()-n查询datetime2(7)字段会排除预期日期? 你猜的完全没错——这就是时间部分在搞鬼!
问题根源拆解
当你在5月17日执行GETDATE()-20时,GETDATE()返回的是当前完整的「日期+时间」(比如2018-05-17 14:30:00.000),减去20天后得到的结果是2018-04-27 14:30:00.000——这个值带着非零的时间部分。
再看你的临时表:DateCol是通过CAST(DATEADD(DAY, ..., @MinDate) AS DATETIME2(7))生成的,@MinDate是DATE类型(没有时间部分),转成datetime2(7)后,时间部分默认是00:00:00.0000000。所以4月27日的DateCol值是2018-04-27 00:00:00.0000000,显然小于2018-04-27 14:30:00.000,自然会被>=条件排除。
而用>= '2018-04-27'时,SQL Server会把字符串隐式转换为datetime2(7)类型,转换后的时间部分同样是00:00:00.0000000,所以4月27日的记录就能被正常包含。
你可以执行这条语句直观验证两者的差异:
SELECT GETDATE()-20 AS [GETDATE()-20的实际值], CAST('2018-04-27' AS DATETIME2(7)) AS [字符串转换后的值]
解决方案:截断时间部分
要让GETDATE()-n的行为和字符串条件一致,核心是去掉GETDATE()的时间部分,推荐几种常用方法:
- 先转成
DATE类型再计算
SELECT COUNT(*) FROM #temp WHERE DateCol >= CAST(GETDATE() AS DATE) - 20
DATE类型没有时间部分,减去20天后得到的是纯日期,和datetime2(7)比较时会自动补全午夜零点的时间,和字符串条件效果完全一致。
- 用日期差截断时间
SELECT COUNT(*) FROM #temp WHERE DateCol >= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 20, 0)
通过计算从0日期到今天的天数,再减去20,最终得到的是20天前的午夜零点时间。
- 先计算再截断时间
SELECT COUNT(*) FROM #temp WHERE DateCol >= CAST(DATEADD(DAY, -20, GETDATE()) AS DATE)
先算出20天前的完整时间,再转成DATE类型去掉时间部分,结果同样是午夜零点。
总结
不管你的datetime2(7)字段时间部分是不是全0,只要GETDATE()-n返回的是带非零时间的值,就会比同日期但时间为0的datetime2(7)值大,导致同日期的记录被排除。所以只要确保过滤条件的时间部分是午夜零点,就能包含当天所有记录。
内容的提问来源于stack exchange,提问作者Santhoshkumar KB

