我正在使用一个糟糕的礼仪CMS系统。
其事件模块可以按明确的日期/时间存储事件,例如2011-08-04 13:30:00
或每周重复发生的事件,其中重复的一天被存储为基于0的整数,并且时间被添加到日期/时间字段(其中数据为0),例如, 0000-00-00 13:30:00
。
我需要在接下来的两周内完成所有活动。这意味着我需要所有每周重复活动以及接下来两周内的明确日期。
我不是一个SQL专家。我写了这个查询...
SELECT `id`,
`name`,
`info`,
`date_time`,
`weekly_day`
FROM `events` AS a
WHERE `active` = true
AND `id` IN (SELECT `id`
FROM `events`
WHERE WEEK(`date_time`) BETWEEN WEEK(NOW()) AND WEEK(
DATE_ADD(NOW(), INTERVAL
1 WEEK)))
OR DATE(`date_time`) = 0
ORDER BY Time_to_sec(IF(TIME(`date_time`), TIME(`date_time`),
Concat(EXTRACT(HOUR FROM `date_time`
), ":",
EXTRACT(MINUTE FROM `date_time`)))),
IF(DATE(`date_time`), Dayofweek(`date_time`), `weekly_day` + 1) ASC
(请注意,上面的NOW()
被替换为一些PHP,并以MySQL格式回显当前日期.MySQL服务器的时区与我想要的时区不同。)
这成功获取了我想要的所有记录,但它没有正确排序。例如,6月9日的事件将在6月2日事件之前出现。
如何修复查询?
干杯。
我想出了这个,但我仍然被卡住了......尽管日期似乎是正确的,但它不会显示每周的事件。
SELECT `id`,
`name`,
`info`,
`date_time`,
`weekly_day`,
( DATE(`date_time`) = 0 ) AS `weekly`
FROM `events`
WHERE `active` = true
AND IF (DATE(`date_time`), WEEK(`date_time`),
DATE_ADD(
Concat(DATE(NOW()), " ", Concat(EXTRACT(HOUR FROM `date_time`),
":",
EXTRACT
(
MINUTE FROM `date_time`))), INTERVAL
`weekly_day`
DAY))
BETWEEN WEEK(
NOW()) AND WEEK(DATE_ADD(NOW(), INTERVAL 2 WEEK))
ORDER BY `date_time` ASC
我无法使用TIME()
来提取时间部分,因为它一直在返回NULL
。
我做错了什么?
答案 0 :(得分:3)
我会分两步完成:
两个子查询的UNION为您提供所有事件及其实际发生日期和时间;正确地订购它是微不足道的。
如果您需要记录信息是否来自定期会议,请在两个子查询的选择列表中保留一些内容(可能是简单的“R”或“N”),这样您就可以判断原始条目是经常性的或非经常性的。
我不是MySQL DATETIME操作方面的专家 - 但我确实知道我在另一个DBMS的那个区域,即Informix。就本讨论中的目的而言,表格中只有两个有趣的列:date_time
和weekly_day
。我假设您使用的是1 =星期日,7 =星期六的MySQL约定。由于我们需要2周的重复事件信息,因此最简单的生成3周并过滤掉不相关的信息。
给定参考日期和一周中的整数天,我们可以使用以下方法从中获取三个值:
refdate - DAYOFWEEK(refdate) + day_of_week
refdate - DAYOFWEEK(refdate) + day_of_week + INTERVAL 7 DAYS
refdate - DAYOFWEEK(refdate) + day_of_week + INTERVAL 14 DAYS
这些产生三个日期;第一个可能在过去,或者第三个可能在将来太远(因此需要丢弃)。这是日期是自参考日期以来的天数(如在Informix中)的算术。要将第三行转换为MySQL,假设'refdate'是NOW()
,我们似乎必须写(非常冗长 - 我认为Informix对于DATETIME操作非常冗长):
DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())), INTERVAL weekly_day DAY),
INTERVAL 14 DAY)
在这里,我假设我可以将weekly_day
中的整数转换为若干天;必要时调整。这些公式为每个重复条目提供三个DATE值,其中两个是相关的。为了增加时间,我认为我们需要:
DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())), INTERVAL weekly_day DAY),
INTERVAL 14 DAY), INTERVAL TIME(date_time) HOUR_SECOND)
因此,我的初始答案中提到的第一个子查询本身需要是一个3路UNION。
SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL 14 DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events
UNION
SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL 7 DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events
UNION
SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events;
您现在需要对“接下来的两周”进行过滤。究竟是哪个日期/时间范围?从参考时刻 - 指定NOW()?或者从参考日开始?或者从参考日后的第二天开始?一切都可能合理;由于代码似乎使用了引用模式(并且它是最简单的),我们将继续使用:
SELECT *
FROM (SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL 14 DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events
UNION
SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL 7 DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events
UNION
SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events) AS r
WHERE r.event_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 14 DAY);
这给出了重复发生的事件。 r.event_time
上的过滤条件可以在UNION的每个分支中下推(复制),但是我会将它保留在原来的位置。
使用以下内容找到非重复发生的事件:
SELECT 'N' AS info_mode,
date_time AS event_time
FROM events AS e
WHERE e.date_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 14 DAY)
AND DATE(e.date_time) != DATE '0000-00-00';
因此,我们可以创建这两个查询的并集。可以将event_time
中的条件从两个子查询中拖出(我们可以希望优化器然后'将其放回')。
而非经常性事件子查询只是成为UNION的第四部分,导致:
SELECT *
FROM (SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL 14 DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events
UNION
SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL 7 DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events
UNION
SELECT 'R' AS info_mode,
DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
INTERVAL weekly_day DAY),
INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
FROM events
UNION
SELECT 'N' AS info_mode,
date_time AS event_time
FROM events
WHERE DATE(e.date_time) != DATE '0000-00-00'
) AS e
WHERE e.event_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 14 DAY);
因此,这应该为您提供事件类型列表(重复,非重复)以及未来14天内发生的事件类型。您只需要将id
,name
和info
列(以及用于调试的原始date_time
和weekly_day
列)添加回每个列UNION中的4个查询,以及e.event_time
上的ORDER BY子句(以及您要添加的任何打破平局的列)。
请关注任何语法或DBMS特定的语义错误;我希望这个概念是清晰的(并且是正确的),即使由于不熟悉MySQL的细节而导致细节不正确。