Chociaż obecnie zaakceptowana odpowiedź była dla mnie ogromną pomocą, chciałem udostępnić kilka przydatnych modyfikacji, które upraszczają zapytania i zwiększają wydajność.
„Proste” powtarzanie zdarzeń
Aby obsłużyć zdarzenia powtarzające się w regularnych odstępach czasu, takie jak:
Repeat every other day
lub
Repeat every week on Tuesday
Powinieneś utworzyć dwie tabele, z których jedna nazywa się events
tak:
ID NAME
1 Sample Event
2 Another Event
I tabela o nazwie events_meta
tak:
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
Z repeat_start
bycia UNIX data datownik bez czasu (1369008000 odpowiada do 20 maja 2013) i repeat_interval
ilości w sekundach pomiędzy przerwami (604800 wynosi 7 dni).
Pętlując każdy dzień w kalendarzu, możesz uzyskać powtarzające się zdarzenia za pomocą tego prostego zapytania:
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE (( 1299736800 - repeat_start) % repeat_interval = 0 )
Wystarczy zastąpić datownikiem uniksowym (1299736800) każdą datę w kalendarzu.
Zwróć uwagę na użycie modulo (znak%). Ten symbol przypomina podział zwykły, ale zwraca iloraz zamiast ilorazu i jako taki wynosi 0, ilekroć bieżąca data jest dokładną wielokrotnością przedziału powtarzania od początku_startu.
Porównanie wydajności
Jest to znacznie szybsze niż poprzednio sugerowana odpowiedź „meta_keys”, która wyglądała następująco:
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
Jeśli uruchomisz WYJAŚNIJ to zapytanie, zauważysz, że wymagało to użycia bufora dołączania:
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 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 |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
Rozwiązanie z 1 złączeniem powyżej nie wymaga takiego bufora.
Wzory „złożone”
Możesz dodać obsługę bardziej złożonych typów, aby obsługiwać te typy reguł powtarzania:
Event A repeats every month on the 3rd of the month starting on March 3, 2011
lub
Event A repeats second Friday of the month starting on March 11, 2011
Tabela wydarzeń może wyglądać dokładnie tak samo:
ID NAME
1 Sample Event
2 Another Event
Następnie, aby dodać obsługę tych złożonych reguł, dodaj kolumny, aby events_meta
:
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
Należy pamiętać, że po prostu trzeba też określić repeat_interval
czy zestaw repeat_year
, repeat_month
, repeat_day
, repeat_week
, oraz repeat_weekday
danych.
To sprawia, że wybór obu typów jednocześnie jest bardzo prosty. Wystarczy zapętlić każdy dzień i wpisać poprawne wartości (1370563200 dla 7 czerwca 2013 r., A następnie rok, miesiąc, dzień, numer tygodnia i dzień tygodnia w następujący sposób):
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
)
Zwraca wszystkie zdarzenia, które powtarzają się w piątek 2. tygodnia, a także wszystkie zdarzenia, które powtarzają się w każdy piątek, więc zwraca zarówno identyfikator zdarzenia 1, jak i 2:
ID NAME
1 Sample Event
2 Another Event
* Sidenote w powyższym SQL użyłem domyślnych indeksów Dnia PHP , czyli „5” w piątek
Mam nadzieję, że to pomaga innym tak samo jak oryginalna odpowiedź pomogła mi!
1299132000
jest zakodowane na stałe? Co to zrobi, jeśli będę musiał uzyskać daty wystąpienia i użytkownika dla danej daty końcowej?