Embora a resposta atualmente aceita tenha sido uma grande ajuda para mim, eu queria compartilhar algumas modificações úteis que simplificam as consultas e também aumentam o desempenho.
Eventos de repetição "simples"
Para manipular eventos que se repetem em intervalos regulares, como:
Repeat every other day
ou
Repeat every week on Tuesday
Você deve criar duas tabelas, uma chamada events
assim:
ID NAME
1 Sample Event
2 Another Event
E uma tabela chamada events_meta
assim:
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
Por repeat_start
ser uma data unix de data e hora sem hora (1369008000 corresponde a 20 de maio de 2013) e repeat_interval
uma quantidade em segundos entre os intervalos (604800 é de 7 dias).
Ao percorrer todos os dias no calendário, você pode obter eventos repetidos usando esta consulta simples:
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE (( 1299736800 - repeat_start) % repeat_interval = 0 )
Basta substituir no timestamp unix (1299736800) por cada data em seu calendário.
Observe o uso do módulo (sinal de%). Esse símbolo é como uma divisão regular, mas retorna o '' restante '' em vez do quociente e, como tal, é 0 sempre que a data atual é um múltiplo exato do repeat_interval do repeat_start.
Comparação de desempenho
Isso é significativamente mais rápido que a resposta baseada em "meta_keys" sugerida anteriormente, que foi a seguinte:
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
Se você executar EXPLAIN esta consulta, notará que é necessário o uso de um buffer de junção:
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 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 |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
A solução com 1 junção acima não requer esse buffer.
Padrões "complexos"
Você pode adicionar suporte para tipos mais complexos para suportar esses tipos de regras de repetição:
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
Sua tabela de eventos pode ter exatamente a mesma aparência:
ID NAME
1 Sample Event
2 Another Event
Em seguida, para adicionar suporte a essas regras complexas, adicione colunas da seguinte events_meta
forma:
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
Note que você só precisa especificar um repeat_interval
ou um conjunto de repeat_year
, repeat_month
, repeat_day
, repeat_week
, e repeat_weekday
dados.
Isso torna a seleção dos dois tipos simultaneamente muito simples. Basta percorrer todos os dias e preencher os valores corretos (1370563200 para 7 de junho de 2013 e depois o ano, mês, dia, número da semana e dia da semana da seguinte forma):
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
)
Isso retorna todos os eventos que se repetem na sexta-feira da 2ª semana, bem como todos os eventos que se repetem toda sexta-feira, portanto, retorna os IDs 1 e 2 do evento:
ID NAME
1 Sample Event
2 Another Event
* Sidenote no SQL acima eu usei índices padrão do dia da semana do PHP Date , então "5" para sexta-feira
Espero que isso ajude os outros tanto quanto a resposta original me ajudou!
1299132000
é codificado? O que isso fará se eu precisar obter as datas de ocorrência e o usuário para a data final especificada?