MS SQL Server的“之间”是否包括范围边界?


234

例如可以

SELECT foo
FROM bar
WHERE foo BETWEEN 5 AND 10

选择5和10还是将它们排除在范围之外?

Answers:


258

BETWEEN运算符包含在内。

从在线书籍:

如果test_expression的值大于或等于begin_expression的值且小于或等于end_expression的值,则BETWEEN返回TRUE。

日期时间警告

注意:使用DateTimes时必须要小心;如果仅给出日期,则该值从当天的午夜开始计算;为避免在结束日期之内错过时间,或避免在多个范围的午夜重复捕获第二天的数据,结束日期应为迄今为止的第二天午夜之前的3毫秒。3毫秒,因为任何小于此值的值都将舍入到第二天的午夜。

例如,要获取2016年6月内的所有值,您需要运行:

where myDateTime between '20160601' and DATEADD(millisecond, -3, '20160701')

where myDateTime between '20160601 00:00:00.000' and '20160630 23:59:59.997'

datetime2和datetimeoffset

从日期减去3毫秒将使您容易受到3毫秒窗口中缺少行的影响。正确的解决方案也是最简单的解决方案:

where myDateTime >= '20160601' AND myDateTime < '20160701'

11
当使用BETWEEN过滤两个日期之间的DateTimes时,您也可以将DateTime强制转换为一个日期,例如:where CONVERT(DATE,MyDate)BETWEEN'2017-09-01'和'2017-09-30'这种方法可以节省时间不相关的DateTime元素
Pete

1
确保不要尝试从日期中减去3毫秒;您会错过这3毫秒内的项目。而且你也不想CONVERT一个日期时间日期,因为这将使得指标无用。使用标准WHERE OrderDate >= '20160601' AND OrderDate < '20160701'。另外,请确保使用yyyymmdd,因为yyyy-mm-dd它取决于语言环境,并且会根据服务器的mdy, dmy, ymd, ydm, myd, and dym设置而被误解。
伊恩·博伊德

254

是的,但是在两个日期之间使用时要小心。

BETWEEN '20090101' AND '20090131'

确实被解释为凌晨12点,或者

BETWEEN '20090101 00:00:00' AND '20090131 00:00:00'

因此会错过1月31日当天发生的任何事情。在这种情况下,您将必须使用:

myDate >= '20090101 00:00:00' AND myDate < '20090201 00:00:00'  --CORRECT!

要么

BETWEEN '20090101 00:00:00' AND '20090131 23:59:59' --WRONG! (see update!)

更新:完全有可能在一天的最后一秒内创建记录,日期时间最晚20090101 23:59:59.997

因此,BETWEEN (firstday) AND (lastday 23:59:59)不建议使用此方法。

请改用该myDate >= (firstday) AND myDate < (Lastday+1)方法。

关于这个问题的文章在这里


1
字符串的类似问题也WHERE col BETWEEN 'a' AND 'z'将排除例如大多数z行。
马丁·史密斯

8
这一点是正确的;但是如果您正在使用日期时间,也就不足为奇了。类似于指出其中BETWEEN 5 AND 10不包括10.2...
Andrzej Doyle

4
CASTdatetime原样运行DATECAST(DATE_TIME_COL AS DATE) BETWEEN '01/01/2009' AND '01/31/2009'
克雷格2014年

2
@craig,的确如此,只要您使用的是SQL 2008或更高版本(即引入了Date数据类型时)。同样,该语法将为每一行转换该值,因此将无法在该字段上使用任何索引(如果这是一个问题)。
BradC 2014年

It is entirely possible to have records created within that last second of the day, with a datetime as late as 01/01/2009 23:59:59.997<-您不能立即使用它,AND '01/31/2009 23:59:59.99999999'还是需要很多9
wal

16

SQL Server 2008中的真实示例。

源数据:

ID    Start
1     2010-04-30 00:00:01.000
2     2010-04-02 00:00:00.000
3     2010-05-01 00:00:00.000
4     2010-07-31 00:00:00.000

查询:

SELECT
    *
FROM
    tbl
WHERE
    Start BETWEEN '2010-04-01 00:00:00' AND '2010-05-01 00:00:00'

结果:

ID    Start
1     2010-04-30 00:00:01.000
2     2010-04-02 00:00:00.000

替代文字


老实说,我没有得到你的答案。如果您发布了一张截图,也许我的互联网提供商已经隐藏了您的截图。
anar khalilov 2014年

2
为什么ID = 3排除行?它的Start值等于BETWEEN上限值,并且BETWEEN是一个包含范围,而不是一个专有上界范围。

结果更好的答案。
山姆

13

如果您遇到了这种情况,而又不想真的尝试在代码中添加一天,那么请让数据库来做。

myDate >= '20090101 00:00:00' AND myDate < DATEADD(day,1,'20090101 00:00:00')

如果您确实包括时间部分:请确保其引用午夜。否则,您可以简单地省略时间:

myDate >= '20090101' AND myDate < DATEADD(day,1,'20090101')

不用担心


12

之间(Transact-SQL)

指定要测试的一个(n)()范围。

test_expression [ NOT ] BETWEEN begin_expression AND end_expression

争论

test_expression

是要测试的表达式,它在begin_expression和end_expression定义的范围内。test_expression必须与begin_expression和end_expression具有相同的数据类型。

NOT

指定否定谓词的结果。

begin_expression

是任何有效的表达式。begin_expression必须与test_expression和end_expression具有相同的数据类型。

end_expression

是任何有效的表达式。end_expression必须与test_expression和begin_expression具有相同的数据类型。

AND

充当占位符,指示test_expression应该在begin_expression和end_expression指示的范围内。

备注

要指定互斥范围,请使用大于(>)和小于(<)运算符。如果对BETWEEN或NOT BETWEEN谓词的任何输入为NULL,则结果为UNKNOWN。

结果值

如果test_expression的值大于或等于begin_expression的值且小于或等于end_expression的值,则BETWEEN返回TRUE。

如果test_expression的值小于begin_expression的值或大于end_expression的值,则NOT BETWEEN返回TRUE。


3

如果列数据类型是日期时间,则可以执行以下操作以从日期时间中消除时间并仅在日期范围之间进行比较。

where cast(getdate() as date) between cast(loginTime as date) and cast(logoutTime as date)

这比将+1添加到结束日期更好。我同意安德鲁·莫顿(Andrew Morton)的观点-如果它不是可靠的,那么更改列数据类型或添加仅包含预先计算日期的第二列可能会提高性能。
亚诺·彼得斯

0

它确实包括边界。

declare @startDate date = cast('15-NOV-2016' as date) 
declare @endDate date = cast('30-NOV-2016' as date)
create table #test (c1 date)
insert into #test values(cast('15-NOV-2016' as date))
insert into #test values(cast('20-NOV-2016' as date))
insert into #test values(cast('30-NOV-2016' as date))
select * from #test where c1 between @startDate and @endDate
drop table #test
RESULT    c1
2016-11-15
2016-11-20
2016-11-30


declare @r1 int  = 10
declare @r2 int  = 15
create table #test1 (c1 int)
insert into #test1 values(10)
insert into #test1 values(15)
insert into #test1 values(11)
select * from #test1 where c1 between @r1 and @r2
drop table #test1
RESULT c1
10
11
15

-3

我一直使用这个:

startDate和(endDate + 1)之间的myDate

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.