SQL BETWEEN 是闭区间,但易被误读为半开区间;数字字段勿加引号;NULL 值致整个条件为 UNKNOWN 而被过滤;边界或字段为 NULL 时需用 COALESCE 或拆分为显式比较。

SQL BETWEEN 是闭区间,但容易被误读为半开区间
很多人写 BETWEEN 时下意识觉得它像编程语言里的 for (i = start; i ,其实不是:BETWEEN a AND b 等价于 column >= a AND column ,两端都包含。问题常出在时间字段上——比如想查“2024-01-01 当天的数据”,写成 WHERE dt BETWEEN '2024-01-01' AND '2024-01-01',看似合理,但若 dt 是 DATETIME 或 TIMESTAMP 类型,实际只命中 '2024-01-01 00:00:00' 这一秒。
- 日期型字段(如
DATE)用BETWEEN '2024-01-01' AND '2024-01-01'是安全的 - 时间型字段(
DATETIME/TIMESTAMP)必须显式指定右边界上限,例如'2024-01-01 23:59:59'或更稳妥的'2024-01-02' - INTERVAL 1 SECOND - 使用
CAST('2024-01-01' AS DATE)强制截断时间部分,再配合范围比较,比依赖BETWEEN更可控
字符串和数字的 BETWEEN 行为一致,但隐式类型转换会埋雷
BETWEEN 对字符串、数字、日期都按各自类型的自然序比较,不自动转类型。陷阱在于:如果字段是字符串类型(如 VARCHAR),而你传入数字字面量,数据库可能触发隐式转换——MySQL 会把字符串转成数字比较,PostgreSQL 则直接报错。
- 字段为
code VARCHAR(10),存的是'001','010','100',执行WHERE code BETWEEN 1 AND 100在 MySQL 中会变成数值比较,结果返回全部三行;但语义上你本意可能是字符串字典序 - 正确做法是统一用字符串字面量:
WHERE code BETWEEN '001' AND '100',此时按字典序比较,'010'会被包含,'001'也会,但'2'就不会(因为'2' > '100') - 数字字段别用引号:
id BETWEEN '1' AND '10'在某些数据库里会触发字符串转数字,但不如直接写id BETWEEN 1 AND 10明确且免去转换开销
NULL 值会让 BETWEEN 整个条件失效
BETWEEN 是一个组合布尔表达式,只要任一操作数为 NULL(比如字段值为 NULL,或边界值来自子查询返回 NULL),整个表达式结果就是 UNKNOWN,而 WHERE 只接受 TRUE 的行,UNKNOWN 和 FALSE 一样被过滤掉。
-
SELECT * FROM orders WHERE amount BETWEEN 100 AND NULL—— 永远不返回任何行,哪怕表里有数据 - 边界来自参数或变量时,务必先判断是否为
NULL,例如用COALESCE(@min, 0)或拆成独立条件:amount >= @min AND @min IS NOT NULL - 字段本身允许
NULL且你想包含这些行?不能靠BETWEEN,得额外用OR amount IS NULL
替代方案往往比 BETWEEN 更清晰、更可控
多数时候,显式写出 >= 和 (或 )反而更不容易出错,尤其涉及时间、可空字段或动态边界时。
- 查某天全量数据:
dt >= '2024-01-01' AND dt 比BETWEEN '2024-01-01' AND '2024-01-01 23:59:59'更简洁、无秒级精度焦虑、兼容所有时间类型 - 带参数的查询中,用两个独立参数
@start和@end,配合col >= @start AND col ,能天然规避NULL边界问题(只要参数未传,就跳过对应条件) - 某些 ORM(如 SQLAlchemy)生成的 SQL 会避免
BETWEEN,优先用拆开的比较,正是出于可预测性和调试友好性考虑
边界到底包不包含,从来不是语法问题,而是你对数据类型、存储精度和业务语义的理解是否到位。写完 BETWEEN 后,花十秒想想字段的实际值长什么样,比背规则有用得多。










