MySQL中的BETWEEN AND操作符是一个范围查询条件,用于筛选字段值在指定范围内的记录。这个语法糖实际上是"大于等于且小于等于"的简写形式,但它在语义表达上更加直观清晰。我处理过不少SQL性能优化的案例,发现很多开发者对这个操作符存在认知误区。
BETWEEN的完整语法是:
sql复制expression BETWEEN lower_bound AND upper_bound
这等价于:
sql复制expression >= lower_bound AND expression <= upper_bound
注意:边界值是包含在内的,这与某些编程语言中的区间表示法不同。比如Python的range(1,10)不包含10,但MySQL的BETWEEN 1 AND 10包含10。
处理订单数据时,经常需要查询金额区间的订单:
sql复制SELECT * FROM orders
WHERE order_amount BETWEEN 1000 AND 5000;
这里有个实际案例:某电商平台发现查询"100-500元"价格区间的商品时,BETWEEN查询比分开写的条件慢15%。经过EXPLAIN分析发现,当字段上有索引时,直接使用>= AND <=的写法有时能获得更好的执行计划。
查询最近一周的订单是最典型的应用:
sql复制SELECT * FROM user_logs
WHERE login_time BETWEEN '2023-06-01 00:00:00' AND '2023-06-07 23:59:59';
关键细节:日期范围查询一定要考虑时间精度。如果只写'2023-06-07',MySQL会默认为'2023-06-07 00:00:00',导致漏掉当天的数据。
虽然不常见,但BETWEEN也可以用于字符串:
sql复制SELECT * FROM products
WHERE product_name BETWEEN 'A' AND 'D';
这会返回所有产品名称以A、B、C开头的记录(直到但不包括以D开头的记录)。
当在已索引列上使用BETWEEN时,要注意:
实测案例:在1000万条用户数据中,查询注册日期范围并筛选状态:
sql复制-- 好的写法(索引有效)
SELECT * FROM users
WHERE register_date BETWEEN '2022-01-01' AND '2022-12-31'
AND status = 1;
-- 差的写法(索引可能失效)
SELECT * FROM users
WHERE YEAR(register_date) = 2022
AND status = 1;
查询不在某个范围内的记录:
sql复制SELECT * FROM products
WHERE price NOT BETWEEN 10 AND 100;
但要注意:NOT操作通常会导致索引失效。在大表上,更好的写法是:
sql复制SELECT * FROM products
WHERE price < 10 OR price > 100;
新手常犯的错误是混淆边界包含逻辑。比如查询某天的数据:
sql复制-- 错误写法(会漏掉23:00-24:00的数据)
WHERE create_time BETWEEN '2023-06-01' AND '2023-06-01'
-- 正确写法
WHERE create_time BETWEEN '2023-06-01 00:00:00' AND '2023-06-01 23:59:59'
当范围过大时,BETWEEN可能导致全表扫描。经验法则是:当范围覆盖超过30%的表数据时,考虑改用其他查询方式。
在跨时区应用中:
sql复制-- 危险!受服务器时区影响
WHERE event_time BETWEEN '2023-06-01' AND '2023-06-02'
-- 更安全的写法
WHERE event_time BETWEEN CONVERT_TZ('2023-06-01', '+00:00', @@session.time_zone)
AND CONVERT_TZ('2023-06-02', '+00:00', @@session.time_zone)
BETWEEN适合连续范围,IN适合离散值:
sql复制-- BETWEEN方式
WHERE age BETWEEN 20 AND 30
-- IN方式(当有明确取值时)
WHERE age IN (20, 25, 30)
在某些复杂场景下,直接使用比较运算符更灵活:
sql复制-- 查询过去30天但排除最近2天的数据
WHERE order_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
AND order_date < DATE_SUB(NOW(), INTERVAL 2 DAY)
某电商商品表有2000万数据,需要优化这个查询:
sql复制SELECT * FROM products
WHERE price BETWEEN 100 AND 500
AND category_id = 5
ORDER BY create_time DESC
LIMIT 100;
优化方案:
sql复制SELECT * FROM products
WHERE category_id = 5
AND price >= 100
AND price <= 500
ORDER BY create_time DESC
LIMIT 100;
实测性能提升8倍,因为:
MySQL 8.0+支持JSON字段的范围查询:
sql复制SELECT * FROM products
WHERE JSON_EXTRACT(specs, '$.weight') BETWEEN 10 AND 20;
对于GIS数据,BETWEEN不适用,应该使用MBRContains等空间函数:
sql复制SELECT * FROM locations
WHERE ST_Within(point, ST_MakeEnvelope(min_lon, min_lat, max_lon, max_lat));
不同MySQL版本对BETWEEN的处理有细微差异:
在编写跨版本应用时,建议显式测试边界条件。