BETWEEN 是什么
BETWEEN a AND b 等价于 x >= a AND x <= b,两个端点都包含。用在数值上很直观,用在时间列上则有一个经典陷阱:BETWEEN '2024-01-01' AND '2024-01-31' 只覆盖到 1 月 31 日零点,当天其余时间的数据会被漏掉。时间范围推荐改用半开区间:>= 起点 AND < 次日。另外 PostgreSQL 提供 BETWEEN SYMMETRIC,不要求左边小于右边,MySQL 没有这个写法。
基本信息
| 等价改写 | x BETWEEN a AND b 即 x >= a AND x <= b |
|---|---|
| 包含性 | 两个端点都包含 |
| NULL 规则 | 任一操作数为 NULL 时结果为 NULL |
| 时间列陷阱 | 用日期做上界只能覆盖到当天零点 |
| 推荐写法 | 时间范围用 >= 起点 AND < 次日 |
| 平台差异 | PostgreSQL 有 BETWEEN SYMMETRIC,MySQL 没有 |
典型使用场景
- 数值区间筛选:金额、分数、数量落在某个范围内
- 时间范围查询:某月、某周的数据(注意用半开区间)
- 分箱统计:按区间归类后用 BETWEEN 判断落在哪一档
- 限流与阈值告警:找出超出阈值区间的记录
- 对账:找出金额在容差范围外的记录
- 索引范围扫描:BETWEEN 是让优化器选 range 访问的常见条件
示例
示例
SELECT * FROM orders WHERE amount BETWEEN 100 AND 500;
-- 时间范围:半开区间不漏数据
SELECT * FROM orders
WHERE created_at >= '2024-01-01'
AND created_at < '2024-02-01';
-- 用日期做闭区间上界会漏掉当天剩余时间
SELECT * FROM orders
WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31'; # 只到 1-31 00:00:00
-- PostgreSQL:BETWEEN SYMMETRIC 自动取两端的最小最大值
SELECT * FROM orders WHERE amount BETWEEN SYMMETRIC 500 AND 100;写法对照
| 需求 | BETWEEN | 推荐写法 |
|---|---|---|
| 数值闭区间 | amount BETWEEN 100 AND 500 | 可用,直观 |
| 自然月数据 | created_at BETWEEN '…01' AND '…31' | 改用 >= 月初 AND < 次月初 |
| 半天数据 | 不直观 | 用 >= 起点 AND < 起点加 12 小时 |
| 左值大于右值 | MySQL 返回空结果 | PostgreSQL 用 BETWEEN SYMMETRIC |
| 字符串范围 | 按排序规则比较 | 注意大小写与排序规则差异 |
BETWEEN 与索引配合良好:在索引列上使用 BETWEEN 通常能让优化器选择 range 访问,比 LIKE 前后加通配符友好得多。
DATETIME 列上用 BETWEEN 加日期字符串,只会覆盖到上界那天的零点,当天其余时间会被漏掉,这是报表数据少一块的常见原因。
BETWEEN 是闭区间,需要排除端点时要写成 > 且 <,不要用 BETWEEN 再想办法修剪。
使用要点
- 时间区间统一用半开区间:>= 起点 AND < 终点
- 写条件前先确认列的类型是日期还是带时间的日期时间
- 确认端点是否要包含,闭区间与半开区间的结果差异要写清
- 字符串范围比较要注意排序规则与大小写设置
- 左值可能大于右值时不要依赖 BETWEEN,先规范化参数
- 索引列上的 BETWEEN 通常能走 range,尽量保持列裸比较