SQL“之间”不包括在内


136

我有这样的查询:

SELECT * FROM Cases WHERE created_at BETWEEN '2013-05-01' AND '2013-05-01'

但是,即使第一天有数据也没有结果。

created_at看起来2013-05-01 22:25:19,我怀疑与时间有关吗?如何解决?

如果我做更大的日期范围,它就可以正常工作,但它也应该(包括一个)使用单个日期。


23
好吧,介于1和1之间的数字是多少?1.5应该在1到1之间吗?只是不要在日期/时间范围内使用BETWEEN。曾经 并要小心评估“效果还不错”-您是否仔细检查了范围内最后一天的结果?如果没有任何时间与它们关联,则仅包括所有行。
亚伦·伯特兰

Answers:


294

具有包容性。您正在将日期时间与日期进行比较。第二个日期被解释为一天开始的午夜。

解决此问题的一种方法是:

SELECT *
FROM Cases
WHERE cast(created_at as date) BETWEEN '2013-05-01' AND '2013-05-01'

解决它的另一种方法是显式二进制比较

SELECT *
FROM Cases
WHERE created_at >= '2013-05-01' AND created_at < '2013-05-02'

亚伦·贝特朗(Aaron Bertrand )在日期(此处)上有一篇很长的博客文章,他在其中讨论了这个日期和其他日期问题。


15
为了完整起见,我建议您使用dateadd来获得第二天的答复。
蒂姆·莱纳

7
@TimLehner为了得到一个好的答案,我将使用'20130501'的ISO-8601格式。对于具有dateformat dmy的非美国用户,您会得到以下信息:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi

2
@RichardTheKiwi-我相信显式位是使用一个在一个结束时(>=)关闭并在另一个()处打开的时间间隔,<并检查超过指定结束日期的一天。
HABO

3
@scottb就我个人而言,每次我想显示,导出,导入或编写带有日期时间的类时,每次都要转换为日期时间会很烦人。我认为SQL Server具有许多内置功能,可以为大多数目的操纵和比较日期时间。
蒂姆·莱纳

10
您永远不要在where子句中强制转换列,因为它会丢失它所拥有的任何索引。这是一个非常糟糕的模式。
Buzinas

49

假定BETWEEN语法中的第二个日期引用被神奇地认为是“一天结束”,但这是不正确的

即这是预期的:

选择*从案例 
WHERE created_at之间的开始 “2013年5月1日”及年底 “2013年5月1日”

但是真正发生的是:

选择*从案例 
在'2013-05-01 00:00:00 + 00000 '和'2013-05-01 00:00:00 + 00000 '之间创建的位置

等效于:

SELECT * FROM Cases WHERE created_at = '2013-05-01 00:00:00 + 00000 '

问题是一种感知/期望,BETWEEN确实包括范围内的下限值和上限值,但是并没有神奇地将日期定为“开始于”或“结束于”。

BETWEEN 在按日期范围过滤时应避免使用。

始终使用>= AND <代替

选择*从案例 
WHERE(created_at > = '20130501' AND created_at < '20130502')

括号在这里是可选的,但在更复杂的查询中可能很重要。


2
不确定在问题已经回答后
能否

5
致编辑;请不要尝试将伪SQL变成代码块,因为注释依赖于BOLD,所以它根本不起作用。
Used_By_Already

2
请再次(对于)编辑者,不要通过伪代码添加虚假的引号;他们不帮助理解。
Used_By_Already

18

您需要执行以下两个选项之一:

  1. 在您的between情况下包括时间部分:(... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'不建议使用...请参见最后一段)
  2. 使用不等式代替between。请注意,然后您必须在第二个值上增加一天:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

我个人的喜好是第二种选择。此外,阿龙贝特朗为什么一个非常明确的解释应该使用它。


6
+1表示2,但-1表示1。这一天的破解完全是不可靠的,也是个坏主意。
亚伦·

1
@AaronBertrand我也更喜欢选项2(我经常使用它)。但是,为什么您说选项1“完全不可靠”?
巴兰卡,

5
请阅读全文。您永远不要将BETWEEN日期时间查询用于包含时间的查询;太多的方法可能会出错。
亚伦·伯特兰


7

我发现将datetime字段与date字段进行比较的最佳解决方案是:

DECLARE @StartDate DATE = '5/1/2013', 
        @EndDate   DATE = '5/1/2013' 

SELECT * 
FROM   cases 
WHERE  Datediff(day, created_at, @StartDate) <= 0 
       AND Datediff(day, created_at, @EndDate) >= 0 

这等效于包含之间的语句,因为它既包含开始日期也包含结束日期,也包括介于两者之间的日期。


3
cast(created_at as date)

仅适用于2008年及更高版本的SQL Server

如果您使用的是旧版本,请使用

convert(varchar, created_at, 101)

2
convert(varchar, created_at, 101)结果就像dd/MM/yyyy和OP的字符串一样yyyy-MM-dd,我认为此答案将无效;)。
shA.t 2015年

2

您可以使用date()从日期时间中提取日期并将结果作为包含日期的功能:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' AND '2013-05-01'

0

SQL查询之间的动态日期

var startDate = '2019-08-22';
var Enddate = '2019-10-22'
     let sql = "SELECT * FROM Cases WHERE created_at BETWEEN '?' AND '?'";
     const users = await mysql.query( sql, [startDate, Enddate]);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.