Obwohl die derzeit akzeptierte Antwort eine große Hilfe für mich war, wollte ich einige nützliche Änderungen mitteilen, die die Abfragen vereinfachen und auch die Leistung erhöhen.
"Einfache" Wiederholungsereignisse
Um Ereignisse zu behandeln, die in regelmäßigen Abständen wiederkehren, wie z. B.:
Repeat every other day
ou
Repeat every week on Tuesday
Sie sollten zwei Tabellen erstellen, eine mit dem Namen events
wie diese:
ID NAME
1 Sample Event
2 Another Event
Und eine Tabelle namens events_meta
wie diese:
ID event_id repeat_start repeat_interval
1 1 1369008000 604800 -- Repeats every Monday after May 20th 2013
1 1 1369008000 604800 -- Also repeats every Friday after May 20th 2013
Mit repeat_start
ein Unix-Zeitstempel-Datum ohne Uhrzeit (1369008000 entspricht dem 20. Mai 2013) und repeat_interval
eine Zeitspanne in Sekunden zwischen den Intervallen (604800 ist 7 Tage).
Durch eine Schleife über jeden Tag im Kalender können Sie mit dieser einfachen Abfrage wiederkehrende Ereignisse ermitteln:
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE (( 1299736800 - repeat_start) % repeat_interval = 0 )
Geben Sie einfach den Unix-Zeitstempel (1299736800) für jedes Datum in Ihrem Kalender ein.
Beachten Sie die Verwendung des Moduls (%-Zeichen). Dieses Symbol entspricht einer normalen Division, gibt aber den "Rest" statt des Quotienten zurück und ist somit immer dann 0, wenn das aktuelle Datum ein exaktes Vielfaches des repeat_interval vom repeat_start ist.
Leistungsvergleich
Dies ist wesentlich schneller als die zuvor vorgeschlagene Antwort auf der Grundlage von "meta_keys", die wie folgt lautete:
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
RIGHT JOIN `events_meta` EM2 ON EM2.`meta_key` = CONCAT( 'repeat_interval_', EM1.`id` )
WHERE EM1.meta_key = 'repeat_start'
AND (
( CASE ( 1299132000 - EM1.`meta_value` )
WHEN 0
THEN 1
ELSE ( 1299132000 - EM1.`meta_value` )
END
) / EM2.`meta_value`
) = 1
Wenn Sie diese Abfrage mit EXPLAIN ausführen, werden Sie feststellen, dass sie die Verwendung eines Join-Puffers erfordert:
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 1 | SIMPLE | EM1 | ALL | NULL | NULL | NULL | NULL | 2 | Using where |
| 1 | SIMPLE | EV | eq_ref | PRIMARY | PRIMARY | 4 | bcs.EM1.event_id | 1 | |
| 1 | SIMPLE | EM2 | ALL | NULL | NULL | NULL | NULL | 2 | Using where; Using join buffer |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
Die obige Lösung mit 1 Verbindung erfordert keinen solchen Puffer.
"Komplexe" Patterns
Sie können die Unterstützung für komplexere Typen hinzufügen, um diese Arten von Wiederholungsregeln zu unterstützen:
Event A repeats every month on the 3rd of the month starting on March 3, 2011
ou
Event A repeats second Friday of the month starting on March 11, 2011
Ihr Veranstaltungstisch kann genau so aussehen:
ID NAME
1 Sample Event
2 Another Event
Um diese komplexen Regeln zu unterstützen, fügen Sie Spalten zu events_meta
etwa so:
ID event_id repeat_start repeat_interval repeat_year repeat_month repeat_day repeat_week repeat_weekday
1 1 1369008000 604800 NULL NULL NULL NULL NULL -- Repeats every Monday after May 20, 2013
1 1 1368144000 604800 NULL NULL NULL NULL NULL -- Repeats every Friday after May 10, 2013
2 2 1369008000 NULL 2013 * * 2 5 -- Repeats on Friday of the 2nd week in every month
Beachten Sie, dass Sie lediglich entweder eine repeat_interval
ou eine Reihe von repeat_year
, repeat_month
, repeat_day
, repeat_week
y repeat_weekday
Daten.
Das macht die Auswahl beider Typen gleichzeitig sehr einfach. Gehen Sie einfach in einer Schleife durch jeden Tag und geben Sie die richtigen Werte ein (1370563200 für den 7. Juni 2013, und dann das Jahr, den Monat, den Tag, die Wochennummer und den Wochentag wie folgt):
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE (( 1370563200 - repeat_start) % repeat_interval = 0 )
OR (
(repeat_year = 2013 OR repeat_year = '*' )
AND
(repeat_month = 6 OR repeat_month = '*' )
AND
(repeat_day = 7 OR repeat_day = '*' )
AND
(repeat_week = 2 OR repeat_week = '*' )
AND
(repeat_weekday = 5 OR repeat_weekday = '*' )
AND repeat_start <= 1370563200
)
Hier werden alle Ereignisse angezeigt, die sich am Freitag der 2. Woche wiederholen, wie auch alle Ereignisse, die sich jeden Freitag wiederholen, so dass sowohl Ereignis-ID 1 als auch 2 zurückgegeben werden:
ID NAME
1 Sample Event
2 Another Event
*Anmerkung: In der obigen SQL habe ich PHP-Datum's Standard-Wochentagsindizes, also "5" für Freitag
Ich hoffe, das hilft anderen so sehr, wie mir die ursprüngliche Antwort geholfen hat!