Хоча прийнята в даний час відповідь була величезною допомогою для мене, я хотів поділитися корисними модифікаціями, які спрощують запити, а також підвищують продуктивність.
"Прості" повторювані події
Обробляти події, які повторюються через рівні проміжки часу, такі як:
Repeat every other day
або
Repeat every week on Tuesday
Ви повинні створити дві таблиці, одну з яких називають events
так:
ID NAME
1 Sample Event
2 Another Event
І таблиця, яка називається events_meta
так:
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
З repeat_start
датою часової позначки Unix, що не має часу (1369008000 відповідає 20 травня 2013 року), а repeat_interval
кількість в інтервалах між інтервалами (604800 - 7 днів).
Переглядаючи кожен день у календарі, ви можете отримувати повторювані події за допомогою цього простого запиту:
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE (( 1299736800 - repeat_start) % repeat_interval = 0 )
Просто замініть на UNIX-timetamp (1299736800) кожну дату вашого календаря.
Зверніть увагу на використання модуля (знак%). Цей символ є як звичайний поділ, але повертає '' залишок '' замість коефіцієнта, і як такий дорівнює 0, коли поточна дата є точним кратним інтервалу повторення від повторного початку.
Порівняння продуктивності
Це значно швидше, ніж запропонована раніше відповідь "meta_keys", яка була такою:
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
Якщо ви запустили EXPLAIN цей запит, ви зазначите, що він вимагав використання буфера приєднання:
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 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 |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
Розчин з 1 з'єднанням вище не потребує такого буфера.
"Складні" візерунки
Ви можете додати підтримку більш складних типів для підтримки таких типів повторних правил:
Event A repeats every month on the 3rd of the month starting on March 3, 2011
або
Event A repeats second Friday of the month starting on March 11, 2011
Ваша таблиця подій може виглядати точно так само:
ID NAME
1 Sample Event
2 Another Event
Потім, щоб додати підтримку цих складних правил, додайте стовпчики 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
Зверніть увагу , що вам просто необхідно або вказати repeat_interval
або набір repeat_year
, repeat_month
, repeat_day
, repeat_week
, іrepeat_weekday
дані.
Це робить вибір обох типів одночасно дуже простим. Просто перегляньте кожен день і заповніть правильні значення (1370563200 за 7 червня 2013 року, а потім рік, місяць, день, число тижня та будній день):
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
)
Це повертає всі події, які повторюються у п’ятницю 2-го тижня, а також будь-які події, які повторюються щоп’ятниці, тому він повертає ідентифікатори події 1 та 2:
ID NAME
1 Sample Event
2 Another Event
* Sidenote у вищевказаному SQL Я використовував стандартні індекси будня PHP Date за замовчуванням, так що "5" для п’ятниці
Сподіваюсь, це допомагає іншим настільки ж, як оригінальна відповідь допомогла мені!
1299132000
твердо кодується? Що це буде робити, якщо мені потрібно отримати дати та термін дії користувача для даної кінцевої дати?