I am using an awful propriety CMS system.
Its events module can store events by an explicit date/time, e.g. 2011-08-04 13:30:00 or by a weekly recurring event, of which the recurring day is stored as a 0 based integer and the time is added to the date/time field (where the data is 0), e.g. 0000-00-00 13:30:00.
I need to get all events upcoming in the next 2 weeks. This means I need all weekly recurring events and the explicit dates which fall in the next 2 weeks.
I’m not much of an SQL expert. I wrote this query…
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
(Note that the NOW() above are replaced with some PHP with echoes the current date in the MySQL format. The MySQL server’s timezone is different to the timezone I want.)
This successfully gets all records I want, but it doesn’t order them correctly. For example, events on the 9th June are appearing before events on the 2nd June.
How can I fix my query?
Cheers.
Update
I’ve came up with this but I am still stuck… it won’t display the weekly events despite the dates appearing to be correct.
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
I couldn’t use TIME() to extract the time portion because it kept returning NULL.
What am I doing wrong?
I would do it in two steps:
The UNION of the two sub-queries gives you all the events with their actual date and time of occurrence; it is trivial to order that correctly.
If you need to record whether or not the information is from a recurring meeting, keep something (probably a simple ‘R’ or ‘N’) in the select-list of the two sub-queries so you can tell whether the original entry was recurring or non-recurring.
I’m not an expert in MySQL DATETIME manipulation – but I do know my away around that area of another DBMS, namely Informix. For my purposes in this discussion, there are only two interesting columns in the table:
date_timeandweekly_day. I assume that you are using the MySQL convention that 1 = Sunday, 7 = Saturday. Since we need 2 weeks’ worth of information for recurring events, it is probably easiest to generate 3 weeks and to filter out the irrelevant ones.Given a reference date and an integer day of the week, we can get three values from it using:
These yield three dates; the first might be in the past or the third might be too far in the future (and hence need discarding). This is the arithmetic when dates are a count of days since a reference date (as in Informix). To translate the third line into MySQL, assuming ‘refdate’ is
NOW(), we seem to have to write (extraordinarily verbosely – I thought Informix was badly verbose for DATETIME manipulation):Here, I’m assuming that I can cast the integer in
weekly_dayto a number of days; adjust if necessary. These formulae give three DATE values for each of the recurring entries, of which two are relevant. To add the time, I think we need:So, the first sub-query mentioned in my initial answer itself needs to be a 3-way UNION.
You now need to filter that for ‘the next two weeks’. Which date/time range is that, exactly? From the reference moment – designated NOW()? Or from the beginning of the reference day? Or from the beginning of the day after the reference day? All could be plausible; since the code appears to use the reference mode (and it is simplest), we’ll go with that:
This gives the recurring events. The filter condition on
r.event_timecould be pushed down (replicated) in each branch of the UNION, but I’ll leave it where it is, pro tempore.The non-recurring events are found using:
Hence, we can create the union of those two queries. It is possible to drag the condition on
event_timeout of the two sub-queries (and we can hope that the optimizer then ‘puts it back’).And the non-recurring events sub-query simply becomes the fourth part of a UNION, leading to:
So, that should give you the list of event types (recurring, non-recurring) and the ones that occur in the next 14 days. You just need to add back the
id,nameandinfocolumns (plus, for debugging, the rawdate_timeandweekly_daycolumns) to each of the 4 queries in the UNION, and an ORDER BY clause one.event_time(and any tie-breaking columns you want to add).Please be kind about any syntax or DBMS-specific semantic errors; I hope the concept is clear (and correct), even if there are details that are not correct because of unfamiliarity with the details of MySQL.