Bien que la réponse actuellement acceptée m'a été d'une grande aide, je voulais partager quelques modifications utiles qui simplifient les requêtes et augmentent également les performances.
Événements de répétition "simples"
Pour gérer des événements qui se reproduisent à intervalles réguliers, tels que:
Repeat every other day
ou
Repeat every week on Tuesday
Vous devez créer deux tables, une appelée events
comme ceci:
ID NAME
1 Sample Event
2 Another Event
Et une table appelée events_meta
comme ceci:
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
Étant repeat_start
une date d'horodatage Unix sans heure (1369008000 correspond au 20 mai 2013), et repeat_interval
un montant en secondes entre les intervalles (604800 est 7 jours).
En parcourant chaque jour du calendrier, vous pouvez obtenir des événements répétés à l'aide de cette simple requête:
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE (( 1299736800 - repeat_start) % repeat_interval = 0 )
Remplacez simplement l'horodatage unix (1299736800) pour chaque date de votre calendrier.
Notez l'utilisation du modulo (signe%). Ce symbole est comme une division régulière, mais renvoie le '' reste '' au lieu du quotient, et en tant que tel est 0 chaque fois que la date actuelle est un multiple exact de repeat_interval de repeat_start.
Comparaison des performances
Ceci est beaucoup plus rapide que la réponse basée sur les "méta-clés" précédemment suggérée, qui était la suivante:
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
Si vous exécutez EXPLAIN cette requête, vous remarquerez qu'elle nécessitait l'utilisation d'un tampon de jointure:
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 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 |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
La solution avec 1 jointure ci-dessus ne nécessite pas un tel tampon.
Modèles "complexes"
Vous pouvez ajouter la prise en charge de types plus complexes pour prendre en charge ces types de règles de répétition:
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
Votre table d'événements peut avoir exactement la même apparence:
ID NAME
1 Sample Event
2 Another Event
Ensuite, pour ajouter la prise en charge de ces règles complexes, ajoutez des colonnes events_meta
comme ceci:
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
Notez que vous simplement devez spécifier un repeat_interval
ou un ensemble de repeat_year
, repeat_month
, repeat_day
, repeat_week
et les repeat_weekday
données.
Cela rend la sélection des deux types simultanément très simple. Parcourez chaque jour et remplissez les valeurs correctes (1370563200 pour le 7 juin 2013, puis l'année, le mois, le jour, le numéro de semaine et le jour de la semaine comme suit):
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
)
Cela renvoie tous les événements qui se répètent le vendredi de la 2e semaine, ainsi que tous les événements qui se répètent tous les vendredis, donc il renvoie les ID d'événement 1 et 2:
ID NAME
1 Sample Event
2 Another Event
* Sidenote dans le SQL ci-dessus, j'ai utilisé les index de jour par défaut de PHP Date , donc "5" pour vendredi
J'espère que cela aide les autres autant que la réponse originale m'a aidé!
1299132000
est codé en dur? Que cela fera-t-il si j'ai besoin d'obtenir les dates d'occurrence et l'utilisateur pour la date de fin donnée?