MySQL的BETWEEN是闭区间,等价于col >= a AND col =与

MySQL 的 BETWEEN 是闭区间,包含边界值
直接说结论:BETWEEN a AND b 等价于 col >= a AND col ,两端都包含。很多人误以为它像某些编程语言的切片那样“左闭右开”,结果漏掉最大值或最小值。
常见错误现象:查 2023-01-01 到 2023-12-31 的订单,写成 date BETWEEN '2023-01-01' AND '2023-12-31',但实际数据含时间(如 '2023-12-31 14:22:05'),这时会漏掉当天所有非零点记录——因为字符串比较时 '2023-12-31 14:22:05' > '2023-12-31'(后者被隐式转为 '2023-12-31 00:00:00')。
- 用日期范围时,显式写完整时间:
BETWEEN '2023-01-01 00:00:00' AND '2023-12-31 23:59:59',或更推荐用>=和组合 -
BETWEEN对NULL无效:若字段为NULL,col BETWEEN 1 AND 10返回FALSE,不是NULL;需单独处理IS NULL - 字符类型按字典序比较,
'b' BETWEEN 'a' AND 'c'成立,但'B' BETWEEN 'a' AND 'c'取决于排序规则(如utf8mb4_0900_as_cs区分大小写)
和 IN、>= + 的性能与语义差异
BETWEEN 本质是语法糖,优化器通常会转成两个边界条件,所以和手写 col >= a AND col 在执行计划里几乎没区别。但它不能替代 IN——BETWEEN 只能表达连续范围,IN (1,3,5) 是离散值。
- 对有索引的数字/日期列,
BETWEEN能走范围扫描(type: range),前提是没在字段上套函数,比如YEAR(created_at) BETWEEN 2022 AND 2023会强制全表扫描 - 不要用
BETWEEN模拟多值等值查询:id BETWEEN 100 AND 105和id IN (100,101,102,103,104,105)看似一样,但前者依赖连续性,后者更明确且容错性强 - 浮点数慎用:
price BETWEEN 9.99 AND 10.01可能因精度问题漏掉10.009999999999999,建议统一转为定点数(DECIMAL)或改用>=/
配合 NOT BETWEEN 时注意逻辑陷阱
NOT BETWEEN a AND b 等价于 col b,不是简单的取反。当字段本身可能为 NULL 时,整个表达式结果为 UNKNOWN,而 WHERE 会过滤掉 UNKNOWN,导致 NULL 行不出现——这点常被忽略。
- 如果想把
NULL当作有效值纳入NOT BETWEEN结果,得显式加判断:col NOT BETWEEN 1 AND 10 OR col IS NULL - 嵌套使用易出错:例如
WHERE status NOT BETWEEN 'active' AND 'pending' AND type = 'user',优先级没问题,但可读性差,建议拆成独立条件或用括号强调 - 对枚举或状态码字段,用
NOT BETWEEN不如直接列排除值:status NOT IN ('active', 'pending')更直观且避免边界歧义
日期范围查询的实用写法(避开 BETWEEN 的坑)
真正安全的日期范围写法往往绕开 BETWEEN,尤其当字段是 DATETIME 或带时区时。核心原则:左闭右开(>= + ),避免时间截断和隐式转换。
- 查整月:用
created_at >= '2023-01-01' AND created_at ,比BETWEEN '2023-01-01' AND '2023-01-31 23:59:59'更可靠 - 查今天:用
DATE(created_at) = CURDATE()效率低(无法走索引),应写成created_at >= CURDATE() AND created_at - 跨时区场景下,先统一转为 UTC 再比较,避免本地时间偏差影响范围判断
边界值处理永远比语法本身更关键——BETWEEN 很简单,但时间精度、字符排序、NULL 传播这些细节,才是线上出问题的地方。










