MySQL查询未来时间:解决跨天数据检索问题
2025-03-10 18:03:52
MySQL 时间查询:动态获取当前时间之后指定小时数的数据
遇到了一个时间查询需求,要求查询结果包含从当前时间开始,未来指定小时数范围内的所有数据,并且需要考虑跨天的情况。简单说,就是既要能查今天,也要能查明天。
问题
现有查询语句可以获取当天未来 24 小时内的数据,挺好。但问题在于,如果指定一个大于当前时间到当天 24 点之间剩余小时数的数值,比如,现在晚上 10 点,你给个hours = 17
,想查未来17小时的,那按理说到第二天下午3点前的数据都要有,但这个查询不会包含第二天的数据。
现在的查询长这样:
SELECT *
FROM jobDetails jd
WHERE TIME(time) > TIME(CONVERT_TZ(NOW(), '+00:00', '+05:00'))
AND TIME(time) <= TIME(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR));
数据库里 time
字段是这么存的:
**time**
19:43
02:50
16:48
18:15
16:37
17:00
16:40
16:42
19:13
21:44
03:25
03:25
03:25
03:25
03:25
问题原因分析
原查询只针对 time
字段的时间部分进行比较,忽略了日期。DATE_ADD
函数虽然增加了小时数,但当 time
字段本身只包含时间信息时,跨天的比较就失效了。原查询没有考虑到 time
字段没有日期。
解决方案
方案一: 改造表结构 (推荐)
如果可以修改表结构,把 time
列改成 DATETIME
或 TIMESTAMP
类型,直接存储日期和时间,那所有问题就简单了! 查询时,可以直接用 >
和 <
来比较。
-
原理:
DATETIME
或TIMESTAMP
类型包含日期和时间信息,可以直接比较,不用拆分日期和时间。 -
代码示例:
先要把表改一下:
ALTER TABLE jobDetails MODIFY COLUMN time DATETIME; -- 如果原来 time 字段的数据只是时间,没有日期,还需要更新一下数据,给他们加上日期。比如加上当天的日期: -- UPDATE jobDetails SET time = CONCAT(CURDATE(), ' ', time);
然后,查询语句就简单多了:
SELECT * FROM jobDetails WHERE time > CONVERT_TZ(NOW(), '+00:00', '+05:00') AND time <= DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR);
-
安全提示: 务必备份数据库,再修改表结构!
方案二: 使用日期和时间函数组合判断
如果不能动表结构,只能在 time
字段上下功夫。基本思路是:分别比较日期和时间。
-
原理: 将当前时间和增加小时数后的时间,都拆分成日期和时间两部分,对
jobDetails
表的time
,也加上日期(当天的或者第二天的),进行组合条件判断。 -
代码示例:
SELECT * FROM jobDetails jd WHERE ( ( DATE(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR)) = CURDATE() -- 如果目标时间还在今天 AND TIME(time) > TIME(CONVERT_TZ(NOW(), '+00:00', '+05:00')) AND TIME(time) <= TIME(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR)) ) OR ( DATE(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR)) > CURDATE() -- 如果目标时间超出了今天 AND ( TIME(time) > TIME(CONVERT_TZ(NOW(), '+00:00', '+05:00')) -- 今天晚些时候的 OR TIME(time) <= TIME(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR)) -- 或者是截止日期的 ) ) );
-
进阶使用技巧:
- 如果经常要执行这类查询,可以考虑创建一个函数或存储过程,把这个复杂的逻辑封装起来,简化使用。
- 针对time列增加索引,但是效果不大。
-
更简洁的方案二sql
SELECT *
FROM jobDetails jd
WHERE
(
DATE(CONVERT_TZ(NOW(), '+00:00', '+05:00')) = CURDATE() AND
TIME(time) > TIME(CONVERT_TZ(NOW(), '+00:00', '+05:00')) AND
TIME(time) <= TIME(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR))
)
OR
(
DATE(CONVERT_TZ(NOW(),'+00:00','+05:00')) < DATE(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR)) and
(
TIME(time) >= TIME(CONVERT_TZ(NOW(), '+00:00', '+05:00'))
OR
TIME(time) <= TIME(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR))
)
);
方案三: 将time转换为当天的datetime类型(DateTime Casting)
如果表结构没法改,也可以先临时把 time
拼成一个完整的 datetime
, 再进行比较。
-
原理 : 将
time
字段值与当前日期或下一天日期拼接, 构建完整的datetime
值,然后进行比较. -
代码示例
SELECT * FROM jobDetails jd WHERE CASE WHEN DATE(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR)) = CURDATE() THEN -- 如果在同一天 CAST(CONCAT(CURDATE(), ' ', TIME(time)) AS DATETIME) > CONVERT_TZ(NOW(), '+00:00', '+05:00') AND CAST(CONCAT(CURDATE(), ' ', TIME(time)) AS DATETIME) <= DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR) ELSE -- 跨天了 ( CAST(CONCAT(CURDATE(), ' ', TIME(time)) AS DATETIME) > CONVERT_TZ(NOW(), '+00:00', '+05:00') OR CAST(CONCAT(DATE_ADD(CURDATE(), INTERVAL 1 DAY), ' ', TIME(time)) AS DATETIME) <= DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR) ) END;
这个方案在MySQL 8 表现不错, 在MySQL 5.7 , 需要稍微调整
CAST
函数的用法。 -
代码示例(兼容MySQL 5.7)
SELECT * FROM jobDetails jd WHERE CASE WHEN DATE(DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR)) = CURDATE() THEN -- 如果在同一天 CONCAT(CURDATE(), ' ', TIME(time)) > CONVERT_TZ(NOW(), '+00:00', '+05:00') AND CONCAT(CURDATE(), ' ', TIME(time)) <= DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR) ELSE -- 跨天了 ( CONCAT(CURDATE(), ' ', TIME(time)) > CONVERT_TZ(NOW(), '+00:00', '+05:00') OR CONCAT(DATE_ADD(CURDATE(), INTERVAL 1 DAY), ' ', TIME(time)) <= DATE_ADD(CONVERT_TZ(NOW(), '+00:00', '+05:00'), INTERVAL ? HOUR) ) END;
注意 :在MySQL5.7版本中不支持直接CAST
到DATETIME
的用法。
- 不推荐使用原因
此方式导致索引失效,无法走索引进行查询。
总结
修改表结构最直接有效,如果改不了表,就用第二或者第三种方案,组合日期和时间判断,麻烦点,但是管用。要注意低版本MySQL数据库中CAST
的用法,避免出错。