引言
BETWEEN 是MySQL中最常用的范围查询操作符之一,用于筛选指定区间内的数据。本文将系统解析其正确用法、执行原理、性能优化方案,并通过真实案例演示如何避免常见的边界陷阱。
一、基础语法与使用规范
1.1 标准语法结构
-- 闭区间查询(包含边界值)
SELECT * FROM orders
WHERE amount BETWEEN 100 AND 500;
-- 等价于:
SELECT * FROM orders
WHERE amount >= 100
AND amount <= 500;
1.2 三大核心应用场景
-- 数值区间(价格/销量等)
SELECT * FROM products
WHERE price BETWEEN 50 AND 200;
-- 日期范围(订单/日志等)
SELECT * FROM user_logs
WHERE login_time BETWEEN '2025-06-01' AND '2025-06-30';
-- 字符串区间(按字母排序)
SELECT * FROM employees
WHERE last_name BETWEEN 'H' AND 'M';
二、三大性能陷阱与优化方案
陷阱1:非索引字段引发全表扫描
问题复现:
-- 未建索引的date字段
SELECT * FROM sales
WHERE sale_date BETWEEN '2025-01-01' AND '2025-01-31';
-- 扫描50万行,耗时1.8s
优化方案:
-- 创建日期索引
ALTER TABLE sales ADD INDEX idx_sale_date(sale_date);
-- 查询耗时降至0.02s
EXPLAIN SELECT * FROM sales
WHERE sale_date BETWEEN ...; -- 使用索引范围扫描(range)
陷阱2:隐式类型转换导致索引失效
错误示范:
-- 字段类型:INT,但用字符串查询
SELECT * FROM products
WHERE id BETWEEN '1000' AND '2000'; -- 触发全表扫描!
解决方案:
-- 保持类型一致
SELECT * FROM products
WHERE id BETWEEN 1000 AND 2000; -- 正确使用索引
陷阱3:超过阈值引发全表扫描
临界点规则:
当BETWEEN覆盖的数据量 > 表总行数20%时,MySQL可能放弃索引走全表扫描
优化策略:
-- 大范围查询改造为分批次处理
SET @min_id = (SELECT MIN(id) FROM orders);
SET @max_id = (SELECT MAX(id) FROM orders);
WHILE @min_id <= @max_id DO
SELECT * FROM orders
WHERE id BETWEEN @min_id AND @min_id + 1000;
SET @min_id = @min_id + 1001;
END WHILE;
三、边界值处理的六大黄金法则
法则1:日期范围包含时间戳的处理
-- 错误:漏掉23:59:59的数据
SELECT * FROM logs
WHERE create_time BETWEEN '2025-06-01' AND '2025-06-30';
-- 正确:包含全天数据
SELECT * FROM logs
WHERE create_time BETWEEN '2025-06-01 00:00:00'
AND '2025-06-30 23:59:59';
法则2:浮点数精度问题规避
-- 错误:浮点数计算可能导致遗漏
SELECT * FROM measurements
WHERE value BETWEEN 0.1 AND 0.3;
-- 正确:扩大边界范围
SELECT * FROM measurements
WHERE value >= 0.099999
AND value <= 0.300001;
法则3:NULL值的处理方案
-- BETWEEN不包含NULL值!
SELECT * FROM users
WHERE age BETWEEN 20 AND 30; -- 返回NULL的行被排除
-- 包含NULL的查询
SELECT * FROM users
WHERE (age BETWEEN 20 AND 30)
OR age IS NULL;
四、高级用法:组合场景实战
案例1:多字段复合范围查询
-- 查询特定时间段的VIP用户订单
SELECT * FROM orders
WHERE order_time BETWEEN '2025-05-01' AND '2025-05-31'
AND user_id IN (
SELECT user_id FROM vip_users
WHERE level BETWEEN 3 AND 5
);
案例2:动态区间统计
-- 统计各价格区间商品数量
SELECT
CASE
WHEN price BETWEEN 0 AND 99 THEN '0-99'
WHEN price BETWEEN 100 AND 499 THEN '100-499'
ELSE '500+'
END AS price_group,
COUNT(*) AS product_count
FROM products
GROUP BY price_group;
案例3:时间滑动窗口分析
-- 近30天每天登录用户统计
SELECT
DATE(login_time) AS day,
COUNT(DISTINCT user_id) AS active_users
FROM user_logs
WHERE login_time BETWEEN CURDATE() - INTERVAL 29 DAY
AND CURDATE() + INTERVAL 1 DAY
GROUP BY day;
五、与其它操作符性能对比
5.1 执行效率实测对比
查询方式 | 10万行耗时 | 1000万行耗时 |
---|---|---|
BETWEEN (索引字段) |
0.002s | 0.15s |
>= AND <= (索引字段) |
0.002s | 0.15s |
IN (连续值) |
0.08s | 超时 |
多条件OR (非连续范围) |
0.5s | 超时 |
5.2 适用场景选择建议
场景 | 推荐操作符 |
---|---|
连续数字/日期范围 | BETWEEN |
非连续值查询 | IN |
单边界查询 | > / < |
复杂逻辑组合 | 多条件 AND/OR |
结语:核心使用原则
- 索引优先:为BETWEEN字段创建索引
- 类型一致:避免隐式类型转换
- 边界确认:明确包含/排除规则
- 范围控制:超20%数据量时分批处理
“范围查询是把双刃剑,精确掌控边界才能发挥最大威力。”
——《高性能MySQL》实践箴言
附录:常用边界函数
-- 获取日期月初/月末
SELECT
LAST_DAY(CURDATE() - INTERVAL 1 MONTH) + INTERVAL 1 DAY AS month_start,
LAST_DAY(CURDATE()) AS month_end;
-- 动态计算7天范围
SELECT * FROM logs
WHERE log_date BETWEEN CURDATE() - INTERVAL 7 DAY
AND CURDATE();