BETWEEN时间范围易漏数据,因隐式补零致右边界截断;应显式写全时间或用>=与<组合,如created_at >= '2023-01-01' AND created_at < '2024-01-01'。

WHERE子句里用BETWEEN写时间范围,但结果不对?
直接用 BETWEEN '2023-01-01' AND '2023-12-31' 看似简洁,但容易漏掉当天的全部数据——因为默认会截断为 '2023-12-31 00:00:00',而你表里的 created_at 可能是 '2023-12-31 14:22:05',它不满足“小于等于”条件。
根本原因是 BETWEEN 是闭区间(包含两端),但时间字段常带时分秒,只写日期字符串会隐式补零,导致右边界被砍掉一整天。
- ✅ 正确做法:显式写出完整时间边界,比如
BETWEEN '2023-01-01 00:00:00' AND '2023-12-31 23:59:59' - ⚠️ 更稳妥写法(推荐):改用
>= AND ,例如 <code>created_at >= '2023-01-01' AND created_at —— 这样既避免秒级精度问题,又天然兼容 <code>TIMESTAMP和DATETIME类型 - ❌ 避免对
DATE类型字段用BETWEEN混合字符串,比如date_col BETWEEN '2023-01-01' AND '2023-12-31'在某些数据库(如 PostgreSQL)中可能因时区或隐式转换出错
PostgreSQL里BETWEEN对timestamptz字段特别容易翻车
PostgreSQL 的 timestamptz 会自动按会话时区做转换,而 BETWEEN 两边字符串如果没有时区标识,会被当作本地时间解析,导致跨时区查询结果漂移。
- ✅ 写死时区:用
BETWEEN '2023-01-01 00:00:00+08' AND '2023-12-31 23:59:59+08' - ✅ 更可靠:统一转为 UTC 比较,比如
created_at AT TIME ZONE 'UTC' BETWEEN '2023-01-01' AND '2023-12-31' - ⚠️ 不要依赖
CURRENT_DATE直接塞进BETWEEN——它返回的是日期类型,和timestamptz比较时会触发隐式 cast,行为不可控
MySQL 8.0+ 中BETWEEN配合索引失效的典型场景
即使你在 created_at 上建了索引,BETWEEN 也可能让优化器放弃使用索引,尤其当右边值是函数或表达式时。
- ❌ 错误示例:
created_at BETWEEN '2023-01-01' AND DATE_ADD(NOW(), INTERVAL -1 DAY)——DATE_ADD是运行时计算,MySQL 无法预估范围,常退化为全表扫描 - ✅ 改成字面量或参数:
created_at BETWEEN ? AND ?(应用层算好两个时间戳传入) - ✅ 或改用确定性函数:
created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND created_at ,这种写法在 MySQL 8.0+ 中能更好利用索引
用BETWEEN查“最近7天”时,别忽略边界当天的0点问题
很多人写 BETWEEN DATE_SUB(NOW(), INTERVAL 6 DAY) AND NOW(),本意是查最近7个自然日,但实际查的是从“6天前此刻”到“现在”,跨度其实是168小时,不是7个完整的日历日。
- ✅ 查完整日历日(比如今天是 2024-04-05,要查 2024-03-30 到 2024-04-05):
DATE(created_at) BETWEEN '2024-03-30' AND '2024-04-05' - ⚠️ 但注意:
DATE(created_at)会导致索引失效,除非你建了函数索引(MySQL 8.0+/PostgreSQL) - ✅ 推荐替代:
created_at >= '2024-03-30' AND created_at —— 简洁、可索引、无歧义
时间范围过滤真正麻烦的从来不是语法,而是你没意识到数据库怎么解释那个“2023-12-31”——它到底代表一整天,还是一个瞬间。

















