MySQL 5:如何用单查询实现工作时段最繁忙30分钟查询
单条SQL实现MySQL中找出最繁忙的30分钟时段
嗨,这个需求完全可以用单条SQL搞定!核心思路是先生成所有可能的30分钟时间区间,再统计每个区间内有多少顾客的停留时间和它重叠。下面是具体实现步骤和代码:
1. 生成30分钟时间区间
MySQL 8.0及以上支持递归CTE(WITH RECURSIVE),咱们可以用它来生成从最早到最晚停留时间范围内的所有30分钟区间:
- 先找到所有顾客中最早的到达时间,向下取整到最近的30分钟(比如10:05就变成10:00)
- 再找到最晚的离开时间,向上取整到最近的30分钟(比如12:20就变成12:30)
- 然后递归生成每一个30分钟的区间,直到覆盖整个时间范围
2. 统计每个区间的顾客数
对于每个生成的30分钟区间,判断顾客的停留时间是否和它有重叠——只要顾客的到达时间早于区间结束,且离开时间晚于区间开始,就说明这个顾客在该区间内停留过。
完整SQL代码
WITH RECURSIVE time_intervals AS ( -- 初始化:取最早的30分钟起始区间 SELECT DATE_FORMAT(DATE_SUB(arrival, INTERVAL MINUTE(arrival) % 30 MINUTE), '%Y-%m-%d %H:%i:00') AS interval_start, DATE_FORMAT(DATE_ADD(DATE_SUB(arrival, INTERVAL MINUTE(arrival) % 30 MINUTE), INTERVAL 30 MINUTE), '%Y-%m-%d %H:%i:00') AS interval_end FROM Customer UNION ALL -- 递归生成后续的30分钟区间 SELECT DATE_ADD(interval_start, INTERVAL 30 MINUTE) AS interval_start, DATE_ADD(interval_end, INTERVAL 30 MINUTE) AS interval_end FROM time_intervals -- 终止条件:区间开始时间不晚于最晚的离开时间(向上取整到30分钟) WHERE interval_start < ( SELECT DATE_FORMAT(DATE_ADD(leaving, INTERVAL (30 - MINUTE(leaving) % 30) % 30 MINUTE), '%Y-%m-%d %H:%i:00') FROM Customer ORDER BY leaving DESC LIMIT 1 ) ) -- 统计每个区间的顾客数 SELECT DATE_FORMAT(ti.interval_start, '%H:%i') AS start_time, DATE_FORMAT(ti.interval_end, '%H:%i') AS end_time, COUNT(DISTINCT c.customerId) AS customer_count FROM time_intervals ti LEFT JOIN Customer c ON c.arrival < ti.interval_end AND c.leaving > ti.interval_start GROUP BY ti.interval_start, ti.interval_end ORDER BY ti.interval_start;
代码解释
- 递归CTE
time_intervals:负责生成所有需要检查的30分钟区间,确保覆盖了所有顾客可能停留的时间段 - 关联条件:
c.arrival < ti.interval_end AND c.leaving > ti.interval_start准确判断顾客停留时间和区间是否重叠,避免漏算或错算 - 格式处理:用
DATE_FORMAT把时间转成你需要的HH:mm格式,方便查看 - 去重统计:用
COUNT(DISTINCT c.customerId)确保同一个顾客在同一个区间只被统计一次
运行这段代码后,就能得到和你示例完全一致的结果啦!如果你的MySQL版本低于8.0,也可以用临时表或数字表来生成区间,但递归CTE是最简洁的方式。
内容的提问来源于stack exchange,提问作者Puser
相关产品推荐
相关产品推荐

