按天、按月筛选数据,看起来只是补一段日期条件,实际最容易出问题的恰恰是这里。很多查询没有报错、执行也正常,却会在月末、跨天或带毫秒精度的场景里悄悄漏数。下面就从 BETWEEN 的实际行为讲起,拆清楚为什么会漏、什么写法更稳,以及动态范围和字符串日期字段该怎么处理。
为什么 BETWEEN 查日期容易漏掉边界数据
直接写 BETWEEN '2024-01-01' AND '2024-01-31',问题通常不在语法,而在数据库对日期字符串的隐式转换。

在 MySQL、PostgreSQL、SQL Server 这类主流数据库里,字符串 '2024-01-31' 通常会被当成 '2024-01-31 00:00:00',而不是很多人直觉里的“这一天的最后一刻”。这样一来,实际查询区间就变成了:
['2024-01-01 00:00:00', '2024-01-31 00:00:00']
这意味着 1 月 31 日当天从 00:00:00 之后产生的数据,尤其是白天和晚间的大部分记录,都不在结果里。对订单表、日志表、行为埋点表来说,这种遗漏往往不会立刻暴露,通常要等到统计结果异常时才被发现。
- 如果字段类型是
DATETIME或TIMESTAMP,只传日期字符串本身就有边界风险 BETWEEN的确是闭区间,但它只会闭合你写出来的两个时间点,不会自动理解成“一整天”- 就算手动补成
23:59:59,也可能漏掉带毫秒或更高精度的记录,比如23:59:59.500
更稳妥的写法:用左闭右开区间
查 2024 年 1 月的全部数据,更推荐下面这种写法:

WHERE order_time >= '2024-01-01' AND order_time < '2024-02-01'
这是典型的左闭右开区间:包含起点,不包含下一个边界。它能自然覆盖从 2024-01-01 00:00:00.000 到 2024-01-31 23:59:59.999... 之间的所有记录,不需要手工猜“最后一秒”该怎么写。
这种方式稳定,原因主要有三点:
>= '2024-01-01'等价于从当天零点开始取数,语义明确< '2024-02-01'直接把右边界推到下一天或下个月的起点,避免处理复杂精度- 对索引更友好,通常比对字段做函数处理更容易走范围扫描
如果字段带时区,例如 TIMESTAMP WITH TIME ZONE,还要确保比较值使用相同时区语义。否则看起来相同的日期范围,实际落到时间线上可能并不一致,结果就会出现偏移。
动态时间范围不要硬写,边界应由函数生成
固定写死字符串只适合临时查询,到了报表、任务调度、接口筛选这类长期使用的场景,边界最好交给函数生成。这样更不容易写错,也更方便复用。
例如“最近 7 天”可以写成:
WHERE log_time >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) AND log_time < CURDATE() + INTERVAL 1 DAY
“本月”则可以写成:
WHERE order_time >= DATE_FORMAT(NOW(), '%Y-%m-01') AND order_time < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH)
这类写法的关键,不是函数本身,而是仍然遵守“左闭右开”的边界设计。只要起点和下一个自然边界算得对,跨天、跨月、跨精度时就不会漏掉尾部数据。
另外,在存储过程或应用层传参时,也不要把时间参数随意当成字符串处理。比如 SQL Server 可声明为 datetime2,MySQL 可声明为 DATETIME。参数类型和字段类型一致,既能减少隐式转换,也更有利于索引命中。
如果字段存的是字符串,先解决类型问题
还有一种更隐蔽的情况:表里根本没有用日期类型,而是把时间存在 VARCHAR 里。这时就算用了 BETWEEN,比较的也往往不是时间先后,而是字符串字典序。

例如日期写成 '20240101'、'2024-01-01',甚至混入 '2024-1-31' 这种不补零格式时,排序和比较结果都可能偏离真实时间顺序。像 '2024-1-31' 排在 '2024-1-2' 前面,这类问题在字符串比较里并不罕见。
如果暂时改不了表结构,至少要先显式转换,再做范围判断,例如 MySQL 可以写成:
WHERE STR_TO_DATE(date_str, '%Y-%m-%d') BETWEEN ...
但这通常只是权宜之计。更长期、也更可靠的做法有两个:
- 直接把字段改成真正的日期时间类型
- 如果短期不能改表,可以增加计算列并建立索引,减少每次查询都做
STR_TO_DATE()的代价
一旦字符串格式不统一,比如同时出现 '2024/01/01' 和 '01-01-2024',问题就不只是漏数,而是整段条件本身已经不再可信。
实际使用时,优先检查这几个判断点
日期边界问题最麻烦的地方,在于 SQL 往往能执行成功,结果却悄悄不完整。遇到统计值异常、日报少数、月底对账不平时,可以优先从下面几个方向排查:
- 字段到底是
DATE、DATETIME、TIMESTAMP,还是字符串 - 查询条件是不是把右边界写成了某天的
00:00:00 - 是否用了
23:59:59这种看似完整、实际挡不住毫秒精度的写法 - 动态时间范围有没有统一采用“起始
>=,下一边界<”的模式 - 参数类型、字段类型、时区设置是否保持一致
归根结底,边界问题不是语法错误,而是语义错误。真正稳妥的办法不是记住几个“差不多能用”的日期写法,而是把时间范围统一成左闭右开区间:从起点开始,到下一自然边界为止。这样无论是查一天、查一月,还是做滚动窗口统计,结果都更可控。









