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

Posted

技术标签:

【中文标题】MS SQL Server“之间”是不是包括范围边界?【英文标题】:Does MS SQL Server's "between" include the range boundaries?MS SQL Server“之间”是否包括范围边界? 【发布时间】:2009-04-14 22:35:20 【问题描述】:

比如可以

SELECT foo
FROM bar
WHERE foo BETWEEN 5 AND 10

选择 5 和 10 或者它们被排除在范围之外?

【问题讨论】:

【参考方案1】:

BETWEEN 运算符具有包容性。

来自在线书籍:

如果值为 test_expression 大于或 等于 begin_expression 的值 并且小于或等于 end_expression。

日期时间警告

注意:使用 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'

【讨论】:

当使用 BETWEEN 过滤两个日期之间的 DateTimes 时,还可以将 DateTime 转换为 Date,例如: where CONVERT(DATE, MyDate) BETWEEN '2017-09-01' and '2017-09 -30' 这种方法使得 DateTime 的时间元素无关 请务必不要尝试从日期中减去 3 毫秒;你会错过这 3 毫秒的项目。而且您也不想将CONVERT datetime 更改为 date,因为这会使索引变得无用。使用标准WHERE OrderDate &gt;= '20160601' AND OrderDate &lt; '20160701'。另外,请务必使用yyyymmdd,因为yyyy-mm-dd 取决于区域设置,并且会根据您服务器的mdy, dmy, ymd, ydm, myd, and dym 设置而被误解。【参考方案2】:

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

BETWEEN '20090101' AND '20090131'

实际上被解释为 12am,或者

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 &gt;= (firstday) AND myDate &lt; (Lastday+1) 方法。

好的article on this issue here。

【讨论】:

字符串的类似问题WHERE col BETWEEN 'a' AND 'z' 将排除大多数 z 行。 这一点当然是对的;但如果您正在使用日期时间,这应该不足为奇。这类似于指出BETWEEN 5 AND 10 不包括10.2... CASTing datetime 作为DATE 可以工作:CAST(DATE_TIME_COL AS DATE) BETWEEN '01/01/2009' AND '01/31/2009' @craig,这是真的,只要您使用的是 SQL 2008 或更高版本,即引入 Date 数据类型的时候。此外,该语法将为每一行转换该值,因此将无法在该字段上使用任何索引(如果这是一个问题)。 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【参考方案3】:

来自 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

【讨论】:

老实说,我没有得到你的回答。如果你发布了截图,也许我的互联网提供商已经隐藏了你的截图。 为什么排除了ID = 3的行?它的Start 值等于BETWEEN 上界值,BETWEEN 是包含范围,而不是独占上界范围。 更好的结果。【参考方案4】:

如果您遇到了这个问题,并且真的不想尝试在代码中添加一天,那么就让数据库来做吧..

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

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

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

不用担心。

【讨论】:

【参考方案5】:

BETWEEN (Transact-SQL)

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

test_expression [ NOT ] BETWEEN begin_expression AND end_expression

参数

test_expression

是在 begin_expression 定义的范围内测试的表达式 和 end_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。

备注

要指定一个独占范围,请使用 大于 (>) 且小于 运算符 (

结果值

如果值为 test_expression 大于或 等于 begin_expression 的值 并且小于或等于 end_expression。

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

【讨论】:

【参考方案6】:

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

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

【讨论】:

这比在结束日期上加 +1 效果更好。我同意 Andrew Morton 的观点 - 如果它不是 sargable,它可能会提高性能以更改列数据类型或添加仅具有预先计算日期的第二列。【参考方案7】:

它确实包括边界。

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

【讨论】:

【参考方案8】:

我一直用这个:

myDate 在 startDate 和 (endDate+1) 之间的位置

【讨论】:

以上是关于MS SQL Server“之间”是不是包括范围边界?的主要内容,如果未能解决你的问题,请参考以下文章

仅使用脚本在 MS SQL Server 之间移动多个表

重新定义 MS Access 文件和 SQL Server 之间的数据链接

SQL Server 地理测试点是不是在范围内 - STWithin() 做啥?

从 MS SQL SERVER 和 MS ACCESS 调用存储过程的结果之间的差异

开始日期和结束日期之间每个项目的 MS SQL Server QUERY SUM

如何获取父子表(MS SQL Server/MSAccess)之间的参考信息?