between and 是闭区间操作符,包含左右边界值;例如 date_col between '2023-01-01' and '2023-12-31' 等价于 date_col >= '2023-01-01' and date_col

SQL中BETWEEN AND对日期的判断是否包含边界值
BETWEEN AND 是闭区间操作符,它**包含左右两个边界值**。也就是说,date_col BETWEEN '2023-01-01' AND '2023-12-31' 等价于 date_col >= '2023-01-01' AND date_col 。这点在处理日期时特别关键——如果你传入的是带时间的 <code>DATETIME 值,而只写了日期字符串(如 '2023-12-31'),数据库通常会隐式转为 '2023-12-31 00:00:00',导致当天 00:00:01 之后的数据被漏掉。
常见错误现象:
查“2023全年订单”,结果少了 12 月 31 日下午的记录。
原因就是用了 BETWEEN '2023-01-01' AND '2023-12-31',但字段是 DATETIME 类型。
- 安全写法是显式写出时间边界:
BETWEEN '2023-01-01 00:00:00' AND '2023-12-31 23:59:59' - 更推荐用半开区间:
date_col >= '2023-01-01' AND date_col ,避免秒级截断和时区歧义 - MySQL 8.0+、PostgreSQL、SQL Server 都遵循标准语义;SQLite 默认也支持,但注意其日期函数返回的是文本,需配合
datetime()转换
不同数据库里日期字面量写法差异
不是所有数据库都接受 '2023-01-01' 这种格式直接参与比较。实际执行前,数据库要先把它解析成内部日期类型,而解析行为依赖系统设置或函数封装。
- MySQL:支持 ISO 格式字符串,但若
sql_mode含STRICT_TRANS_TABLES,非法日期(如'2023-02-30')会报错Incorrect date value - PostgreSQL:严格校验,
'2023-02-30'直接报错;推荐用DATE '2023-01-01'字面量语法,更明确 - SQL Server:接受
'2023/01/01'、'01-01-2023',但受DATEFORMAT和语言影响;稳妥起见用CONVERT(DATE, '20230101', 112) - Oracle:必须用
TO_DATE('2023-01-01', 'YYYY-MM-DD'),否则当作字符串比较,结果不可靠
用BETWEEN判断日期时容易忽略的时区问题
如果表中存的是带时区的 TIMESTAMP WITH TIME ZONE(如 PostgreSQL 的 timestamptz),而你用不带时区的字符串做 BETWEEN,数据库会按当前会话时区自动转换——这会导致同一句 SQL 在不同时区服务器上返回不同结果。
- 现象:开发环境查出 10 条,生产环境(UTC)只查出 7 条
- 根本原因:
'2023-01-01'被解释为会话时区的午夜,再转成 UTC 后可能变成前一日 23:00 - 解决办法:统一用 UTC 字符串 + 显式类型转换,例如 PostgreSQL 中写
col BETWEEN '2023-01-01 UTC'::timestamptz AND '2023-01-02 UTC'::timestamptz - 或者干脆避开
BETWEEN,改用col >= $start AT TIME ZONE 'UTC' AND col
性能考虑:BETWEEN能否走日期字段索引
只要左右边界是确定值(非子查询、非函数调用),且字段本身有索引,BETWEEN 就能有效利用 B-tree 索引进行范围扫描。但要注意几个破坏索引的典型写法:
- 对字段用函数:
DATE(created_at) BETWEEN '2023-01-01' AND '2023-12-31'→ 全表扫描 - 字段参与表达式:
created_at + INTERVAL '1 day' BETWEEN ...→ 同样无法走索引 - 边界含参数但参数类型不匹配:比如字段是
DATETIME,传入参数却是字符串且未加类型提示,某些驱动会触发隐式转换,干扰优化器选择 - MySQL 中若字段是
DATE类型,而条件写成BETWEEN '2023-01-01 00:00:00' AND ...,也可能因类型转换略降效率
最稳的方式始终是:字段原样出现,边界用对应类型的字面量或参数绑定,且确保两者精度一致。










