BETWEEN AND是闭区间,包含左右端点;但对日期字段使用BETWEEN '2024-01-01' AND '2024-01-31'等价于>= '2024-01-01 00:00:00' AND <= '2024-01-31 00:00:00',易漏掉当日其他时间记录。

MySQL中BETWEEN AND对时间字段的边界行为必须明确
BETWEEN AND 是闭区间,包含左右端点。但很多人误以为它会自动处理时间精度问题,结果漏掉当天最后一条记录。比如用 BETWEEN '2024-01-01' AND '2024-01-31' 查询日期字段,实际等价于 >= '2024-01-01 00:00:00' AND —— 末尾的 <code>00:00:00 会让所有 '2024-01-31 14:22:05' 这类数据被排除。
- 日期类型(
DATE)字段:可安全使用BETWEEN '2024-01-01' AND '2024-01-31',MySQL 会隐式补全为'2024-01-01 00:00:00'和'2024-01-31 23:59:59'?错,它不会——仍按00:00:00截断 - 时间类型(
DATETIME/TIMESTAMP)字段:必须显式写出完整时间边界,否则大概率丢数据 - 更稳妥写法是避开
BETWEEN,改用>= '2024-01-01' AND ,逻辑清晰且天然覆盖全天
时区不一致会导致BETWEEN结果意外为空
如果表字段是 TIMESTAMP,而客户端连接时区设为 +08:00,但服务器默认是 SYSTEM(可能为 UTC),BETWEEN 的字面值会被自动转换。例如执行 SELECT * FROM logs WHERE created_at BETWEEN '2024-01-01' AND '2024-01-02';,MySQL 实际比较的是转换后的 UTC 时间,而非你肉眼看到的本地时间。
- 查当前会话时区:
SELECT @@session.time_zone; - 临时统一时区:
SET time_zone = '+08:00';(仅对当前连接生效) - 生产环境建议统一用 UTC 存储,查询时再用
CONVERT_TZ()转换,避免BETWEEN边界漂移
索引失效:BETWEEN在函数包裹下完全不走索引
常见错误是把时间字段套上函数再用于 BETWEEN,比如 WHERE DATE(created_at) BETWEEN '2024-01-01' AND '2024-01-31'。这时 MySQL 无法使用 created_at 上的索引,只能全表扫描。
- 正确做法:确保左侧是裸字段,右侧用合适的时间字面量,如
created_at >= '2024-01-01' AND created_at - 若必须按日期查(忽略时分秒),且字段无索引,可建函数索引(MySQL 8.0+):
CREATE INDEX idx_created_date ON logs ((DATE(created_at))); - 低版本 MySQL 可冗余一个生成列:
ALTER TABLE logs ADD COLUMN created_date DATE AS (DATE(created_at)) STORED;,再对它建索引
字符串格式时间用BETWEEN极容易出错
当时间存为 VARCHAR(如 '20240101142205' 或 '2024-01-01 14:22:05'),直接 BETWEEN 会按字符串字典序比较。虽然 ISO 格式('YYYY-MM-DD HH:MM:SS')在多数情况下能碰巧正确,但一旦出现 '2024-1-1' 或 '2024/01/01' 就彻底乱序。
- 立刻修正存储类型:用
ALTER TABLE ... MODIFY created_at DATETIME;转换(需先清理非法值) - 应急查询可用
STR_TO_DATE()强转,但注意性能差且无法走索引:WHERE STR_TO_DATE(log_time, '%Y-%m-%d %H:%i:%s') BETWEEN '2024-01-01' AND '2024-01-31' - 永远不要依赖字符串时间的字典序做范围判断——它只在格式绝对统一、零填充严格、分隔符固定的前提下才“看起来”可靠
最常被忽略的一点:BETWEEN 的两个操作数类型必须兼容。混用 DATE 和 DATETIME 常引发隐式转换,导致索引失效或结果偏差。动手前先 DESCRIBE table_name; 确认字段类型,再决定怎么写边界值。


















