要查出某段时间内的订单、日志或注册用户,是 MySQL 业务查询里最高频的需求之一。三种主流写法在边界处理、索引适用和出错概率上的差别很明显:本文用建表插样数据的最小示例,讲清 BETWEEN、>= AND <= 以及 DATE 函数各自的边界,并给出可直接复制的排错清单。日期边界是否包含、时间部分会不会被截断、索引会不会失效,是新手最容易翻车的三处。先确认 MySQL 版本与表结构,再选写法,最后用预期输出验证结果。

一、先看结论:MySQL 日期范围查询怎么选
下面这张表直接给出 MySQL 日期范围查询的选择顺序,避免你在边界与索引适用上踩坑,三种写法的对比见配图。想先把 SQL 基础补齐,可以看 MySQL 基础教程。
| 方法 | 适用场景 | 注意点 |
|---|---|---|
BETWEEN ... AND ... |
起止都给到当天边界、人工补时间 | 两端都包含,结束日不补时间会漏掉当天数据 |
>= AND <= |
想精确控制边界、兼顾索引 | 结束值要补到 23:59:59,或改用 < 下月1号 |
DATE(列) 函数 |
只按日期忽略时间、临时排查 | 对列包函数会让索引失效,性能下降,大表易失败 |

如果你要跑在线上、表又大,优先用 >= AND <= 做范围比较;BETWEEN 写法直观但要注意补时间,DATE(列) 只建议在临时排查时用。需要提醒的是,这三类写法的关键差别不在「能不能查出来」,而在「查出来的结果是否完整」以及「在大表上会不会拖垮性能」——这两点正是选型时最该对比的维度。
二、环境确认与最小示例
写 SQL 前先确认运行环境,能少走很多弯路。本文示例基于 MySQL 5.7 与 8.0,日期函数行为一致;如果你用的是更老的 5.6,建表语法相同,但部分时间函数支持更少,需要检查版本后再动手。MySQL 版本差异对日期边界处理基本无影响,但时区配置会,这是必须提前确认的边界。
下面给出建表、插样和一条验证查询,能直接跑。以下为预期结果,未实际运行,按官方文档推断,查询应返回 3 行 9 月订单,10 月那一条被正确排除。
# 进入 MySQL 命令行客户端,准备执行下面的 SQL
mysql -u root -p demo_db
-- 确认 MySQL 版本,5.7 与 8.0 的日期函数行为一致,可放心使用
SELECT VERSION();
-- 建表:订单表,created_at 用 DATETIME 保留时间部分
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(32),
created_at DATETIME -- 时间部分会影响范围边界,别用 DATE 类型丢精度
);
-- 插入样例数据,覆盖边界当天与跨月,便于验证三种写法
INSERT INTO orders (order_no, created_at) VALUES
('A001', '2026-09-01 08:30:00'),
('A002', '2026-09-15 12:00:00'),
('A003', '2026-09-30 23:59:00'), -- 9 月最后一天深夜,用来验证边界是否包含
('A004', '2026-10-01 00:05:00'); -- 10 月,用来验证是否被正确排除
配置好表之后,先用一条最朴素的查询确认数据落库,再往下看三种写法,避免拿空表排错。这一步的检查很关键,能避免后面用错数据来验证边界。
三、方法一:BETWEEN(最直观,注意补时间)
BETWEEN 是闭区间,左右两端都包含,写法最短。范围查询本质是一条带条件的查询语句,先把 WHERE 条件写对再考虑边界;想在线试跑可用 在线代码实例 验证实际结果。但它也是新手漏数据的重灾区——问题出在时间部分。与 BETWEEN 相比,>= AND <= 对边界的控制更显式,不容易踩坑。
-- 方法一:BETWEEN,两端都包含
-- 关键:结束值必须补到 23:59:59,否则 9-30 当天 00:00 之后的数据会被排除
SELECT order_id, order_no, created_at
FROM orders
WHERE created_at BETWEEN '2026-09-01 00:00:00'
AND '2026-09-30 23:59:59';
-- 预期返回 A001、A002、A003 共 3 行;A004 因跨月被排除
常见失败:把结束值写成 '2026-09-30',MySQL 会把它当成 '2026-09-30 00:00:00',导致当天 00:00 之后的订单全部丢失。修复方向是把结束边界补到 23:59:59,或改用下一节的 < 下月1号。这个失败属于典型的日期边界处理不当,排错时先打印实际参与比较的值就能定位。
四、方法二:>= AND <=(边界最清晰,索引友好)
用显式比较代替 BETWEEN,边界一目了然,也更容易写出能命中索引的范围条件。它的适用场景是生产环境、对结果完整性要求高的查询。
-- 方法二:>= 与 <= 显式比较,边界最清晰
SELECT order_id, order_no, created_at
FROM orders
WHERE created_at >= '2026-09-01 00:00:00'
AND created_at <= '2026-09-30 23:59:59';
-- 结果与 BETWEEN 相同;想彻底避开补时间的坑,可改写成 < 下月1号(见排错表)
如果想省掉补 23:59:59 的麻烦,把结束条件换成开区间更稳:
-- 等价写法:结束用 < 下月1号 的零点,天然包含 9-30 全天
SELECT order_id, order_no, created_at
FROM orders
WHERE created_at >= '2026-09-01 00:00:00'
AND created_at < '2026-10-01 00:00:00';
这种半开区间对索引最友好,也是生产环境推荐的范围查询写法,建议验证后直接用于线上。注意:开区间写法把「是否等于结束日 23:59:59」的纠结,变成「是否小于下月 1 号 0 点」,逻辑更干净,也不会因漏补一秒而丢数据。
五、方法三与常见排错
DATE 函数虽然写法最直观,但代价明显。DATE(列) 会把时间部分直接抹掉,只按日期比较,写法最省事,但代价是索引失效。DATE 函数对列套了一层处理,优化器无法再走 created_at 上的索引。
-- 方法三:DATE() 把时间部分抹掉,只按日期比较
SELECT order_id, order_no, created_at
FROM orders
WHERE DATE(created_at) BETWEEN '2026-09-01' AND '2026-09-30';
-- 写法直观,但对列套函数会导致索引失效,仅建议临时排查使用
一旦对列包函数,大表上查询很容易失败或超时。遇到范围查询异常,按下面清单排查(这里的失败/修复均来自真实边界场景,未在本机执行,按官方文档推断):
| 现象 | 常见原因 | 修复方向 |
|---|---|---|
报 Incorrect datetime value |
日期字符串格式不对,如写成 2026/09/01 |
统一用 YYYY-MM-DD HH:MM:SS 标准格式 |
| 结束当天数据丢失 | BETWEEN ... AND '2026-09-30' 时间被当成 00:00:00 |
结束值补到 23:59:59 或改用 < '2026-10-01' |
| 查询很慢、全表扫描 | 对列用了 DATE() / DATE_FORMAT(),索引失效 |
改为范围比较,避免对列包函数 |
| 不同服务器结果偏移 | 连接时区与存储时区不一致 | 检查 time_zone 配置,统一时区后重试 |
以上对比能帮你快速锁定失败类型,尤其是「索引失效」这类性能问题,靠肉眼看不出,必须用 EXPLAIN 验证执行计划。
总结
MySQL 日期范围查询的核心结论很清晰:用 BETWEEN 或 >= AND <= 做范围比较时,务必把结束边界补到当天 23:59:59,或直接改成 < 下月1号,才能完整包含边界当天;DATE(列) 写法虽然直观,却会让索引失效、拖慢查询。选型时先确认 MySQL 版本与表结构,用建表插样的最小示例验证输出,再对照上面的排错表处理日期格式、时区与隐式转换问题。把日期边界和时间部分记牢,能避开绝大多数范围查询的坑。
延伸学习
想在编程狮系统学 MySQL,可以顺着下面三篇深入:
常见问题
Q:BETWEEN 为什么老是漏掉结束当天的数据?
A:BETWEEN 两端都包含,但如果你写 BETWEEN '2026-09-01' AND '2026-09-30',MySQL 会把结束值当成 2026-09-30 00:00:00,当天 00:00 之后的数据全部被排除。修复方法是把结束边界补到 23:59:59,或改用 < '2026-10-01'。这是日期边界处理最常见的失败。
Q:为什么给 created_at 加了索引,查询还是很慢?
A:如果你写成 WHERE DATE(created_at) = '2026-09-01',对列套了函数会导致索引失效,MySQL 只能全表扫描。修复方向是改成范围比较 created_at >= '2026-09-01' AND created_at < '2026-09-02',让索引正常生效。可用 EXPLAIN 验证是否走了索引。
Q:同样的 SQL 在不同时区服务器上结果不一样怎么办?
A:这是时区配置不一致导致的。先检查 SELECT @@time_zone; 确认连接时区,再和存储时区对齐;写入时统一约定时区,查询前用 SET time_zone 配置一致,避免日期被整体偏移。时区属于容易忽视的边界,排错时优先确认。
Q:DATE 函数到底能不能用于线上查询?
A:临时排查没问题,但线上大表不建议。DATE 函数会让索引失效,数据量上去后查询很容易超时失败。如果一定要按天聚合,可用范围比较包住一整天(如 >= 当天 00:00:00 AND < 次日 00:00:00),既命中索引又不丢精度。

TRAE-AI编程



