编写MySQL查询:按用户ID和日期范围返回每日事件记录
解决方案:将用户事件表转换为按天的独立记录
没问题,我来帮你实现这个需求!我们的目标是把原表中按「日期范围+星期标记」存储的记录,拆分成指定日期范围内每个事件发生日的独立行。下面分两种场景给出实现方案,适配不同版本的MySQL:
方法1:使用递归CTE(MySQL 8.0+推荐)
递归CTE是MySQL 8.0及以上版本支持的特性,能轻松生成连续的日期序列,再关联原表筛选出符合条件的日期。
假设你的表名为user_events,执行以下查询(记得替换注释中的参数):
WITH RECURSIVE date_range AS ( -- 替换为你需要查询的起始日期 SELECT '2018-02-20' AS event_date UNION ALL SELECT DATE_ADD(event_date, INTERVAL 1 DAY) FROM date_range -- 替换为你需要查询的结束日期 WHERE event_date <= '2018-03-15' ) SELECT u.USER_ID, dr.event_date FROM date_range dr JOIN user_events u ON dr.event_date BETWEEN u.START_DATE AND u.END_DATE WHERE -- 替换为你指定的USER_ID u.USER_ID = 1 AND ( -- 匹配周一(DAYOFWEEK返回2)且MON列=1 (DAYOFWEEK(dr.event_date) = 2 AND u.MON = 1) -- 匹配周二(DAYOFWEEK返回3)且TUE列=1 OR (DAYOFWEEK(dr.event_date) = 3 AND u.TUE = 1) -- 匹配周三(DAYOFWEEK返回4)且WED列=1 OR (DAYOFWEEK(dr.event_date) = 4 AND u.WED = 1) -- 匹配周四(DAYOFWEEK返回5)且THU列=1 OR (DAYOFWEEK(dr.event_date) = 5 AND u.THU = 1) -- 匹配周五(DAYOFWEEK返回6)且FRI列=1 OR (DAYOFWEEK(dr.event_date) = 6 AND u.FRI = 1) -- 匹配周六(DAYOFWEEK返回7)且SAT列=1 OR (DAYOFWEEK(dr.event_date) = 7 AND u.SAT = 1) -- 匹配周日(DAYOFWEEK返回1)且SUN列=1 OR (DAYOFWEEK(dr.event_date) = 1 AND u.SUN = 1) ) ORDER BY dr.event_date;
代码说明:
- 递归CTE
date_range:自动生成从起始日期到结束日期的所有连续日期,确保不会遗漏任何一天。 - 关联原表:只保留落在用户的
START_DATE和END_DATE范围内的日期。 - 条件筛选:通过
DAYOFWEEK()函数判断日期对应的星期,再匹配原表中对应星期列的1/0标记,筛选出事件实际发生的日期。 - 排序:按日期升序排列结果,方便查看时间线。
方法2:使用数字表(兼容MySQL 5.x版本)
如果你的MySQL版本低于8.0,不支持递归CTE,可以用手动构造的数字表来生成日期序列:
SELECT u.USER_ID, DATE_ADD('2018-02-20', INTERVAL n.n DAY) AS event_date FROM -- 构造数字表,覆盖足够多的天数(这里最多24天,可按需扩展) (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24) n JOIN user_events u ON DATE_ADD('2018-02-20', INTERVAL n.n DAY) BETWEEN u.START_DATE AND u.END_DATE WHERE u.USER_ID = 1 -- 替换为指定的USER_ID AND ( (DAYOFWEEK(DATE_ADD('2018-02-20', INTERVAL n.n DAY)) = 2 AND u.MON = 1) OR (DAYOFWEEK(DATE_ADD('2018-02-20', INTERVAL n.n DAY)) = 3 AND u.TUE = 1) OR (DAYOFWEEK(DATE_ADD('2018-02-20', INTERVAL n.n DAY)) = 4 AND u.WED = 1) OR (DAYOFWEEK(DATE_ADD('2018-02-20', INTERVAL n.n DAY)) = 5 AND u.THU = 1) OR (DAYOFWEEK(DATE_ADD('2018-02-20', INTERVAL n.n DAY)) = 6 AND u.FRI = 1) OR (DAYOFWEEK(DATE_ADD('2018-02-20', INTERVAL n.n DAY)) = 7 AND u.SAT = 1) OR (DAYOFWEEK(DATE_ADD('2018-02-20', INTERVAL n.n DAY)) = 1 AND u.SUN = 1) ) ORDER BY event_date;
注意事项:
- 数字表中的行数需要足够覆盖你查询的日期范围天数,比如如果日期跨度是30天,就要把数字表扩展到包含0-29的数字。
- 核心筛选逻辑和方法1完全一致,只是生成日期的方式不同。
内容的提问来源于stack exchange,提问作者Cyril Byrne
相关产品推荐
相关产品推荐

