日期范围边界怎么取
按时间过滤推荐半开区间 [起点, 下一天或下月):created_at >= '2024-06-01' AND created_at < '2024-07-01'。写成 BETWEEN '2024-06-01' AND '2024-06-30' 会漏掉 6 月 30 日当天带时分秒的数据,因为 '2024-06-30' 等价于那天的 00:00:00。
基本信息
| 推荐写法 | 左闭右开:>= 起点 AND < 终点 |
|---|---|
| 原因 | 带时间的列上用闭区间会漏掉当天剩余时段的数据 |
| DATE 列例外 | 列本身是 DATE 类型时,BETWEEN a AND b 是安全的 |
| 索引 | 范围条件能走 B+ 树索引,前提是过滤列没有被函数包裹 |
| 月末处理 | 终点统一写下个月 1 日,不必计算当月天数 |
| 时区 | 按天过滤前先确认以哪个时区作为日界 |
推荐写法
推荐写法
-- 2024 年 6 月整月:包含当月最后一天的每一秒与毫秒
SELECT * FROM orders
WHERE created_at >= '2024-06-01'
AND created_at < '2024-07-01';
-- 单日:同样用次日作为开区间上界
SELECT * FROM orders
WHERE created_at >= '2024-06-15'
AND created_at < '2024-06-16';常见错误写法
常见错误写法
-- 漏数据:'2024-06-30' 等于当天 00:00:00,6-30 09:00 的记录不会被返回
SELECT * FROM orders
WHERE created_at BETWEEN '2024-06-01' AND '2024-06-30';
-- 月末日期写错:6 月没有 31 日,语义已偏,严格模式还可能直接报错
SELECT * FROM orders
WHERE created_at BETWEEN '2024-06-01' AND '2024-06-31';常见写法对比
| 写法 | 结果 | 评价 |
|---|---|---|
| BETWEEN a AND b | 闭区间,两端都含 | DATE 列安全;DATETIME 列漏月末当天 |
| >= a AND < b | 左闭右开 | 推荐,任何时间精度都正确 |
| >= a AND <= '23:59:59' | 右端精度不足 | 毫秒与微秒记录仍会被漏掉 |
| DATE(col) = '2024-06-01' | 只命中当天 00:00:00 | 语义错,且列上包函数导致索引失效 |
| DATE(col) BETWEEN … | 能表达按天范围 | 列被函数包裹,索引同样用不上 |
典型使用场景
- 日报周报月报的取数范围定义
- 分批导出历史数据:按创建时间切片逐步导出
- 对账:取某个自然月的全部流水
- 增量同步:用上次同步时间点作为左边界
- 数据清理:删除某时间点之前的数据,删除前先确认边界与行数
- 监控告警统计:取最近 24 小时或者最近 7 整天
想按自然月取数,与其计算月末是 28 还是 31 日,不如直接写下个月 1 日作为上界;也可以用日期函数动态生成两个边界,注意别在过滤列本身上包函数。
跨时区按天过滤要先统一日界:存储是 UTC、业务按北京时间看日报时,边界必须换算成对应的 UTC 时刻,否则首尾那一小时的数据会被算错。