Eventos recorrentes / repetidos do calendário - o melhor método de armazenamento


311

Estou construindo um sistema de eventos personalizado e, se você tiver um evento repetido, parecido com este:

O evento A se repete a cada 4 dias, a partir de 3 de março de 2011

ou

O evento B se repete a cada 2 semanas na terça-feira, começando em 1 de março de 2011

Como posso armazenar isso em um banco de dados de uma maneira que simplifique a pesquisa. Não quero problemas de desempenho se houver um grande número de eventos e tenho que passar por todos ao renderizar o calendário.


Você pode explicar por que 1299132000 é codificado? O que isso fará se eu precisar obter as datas de ocorrência e o usuário para a data final especificada?
Murali Murugesan

@Murali Boy, isso é antigo, mas tenho certeza de que 1299132000 deve ser a data atual.
Brandon Wamboldt

@BrandonWamboldt, tentei sua ideia com o SQL Server. stackoverflow.com/questions/20286332/display-next-event-date . Eu quero encontrar todos os próximos itens como c # versão
Billa

Respostas:


211

Armazenando padrões de repetição "simples"

Para o meu calendário baseado em PHP / MySQL, eu queria armazenar informações de eventos repetidos / recorrentes da forma mais eficiente possível. Eu não queria ter um grande número de linhas e queria pesquisar facilmente todos os eventos que ocorreriam em uma data específica.

O método abaixo é grande em armazenar repetir as informações que ocorre em intervalos regulares, como todos os dias, a cada n dias, cada semana, cada mês a cada ano, etc etc. Isto inclui todas as terças e quintas-feiras padrões tipo, bem como, porque eles são armazenados separadamente, todas as semanas, começando na terça-feira e toda semana, começando na quinta-feira.

Supondo que eu tenha duas tabelas, uma chamada eventsassim:

ID    NAME
1     Sample Event
2     Another Event

E uma tabela chamada events_metaassim:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1299132000
2     1             repeat_interval_1  432000

Com repeat_start sendo uma data sem hora como carimbo de data / hora unix e repeat_interval, uma quantidade em segundos entre os intervalos (432000 é de 5 dias).

repeat_interval_1 acompanha o repeat_start do ID 1. Portanto, se eu tiver um evento que se repita toda terça e quinta-feira, o repeat_interval seria 604800 (7 dias) e haveria 2 repeat_starts e 2 repeat_intervals. A tabela ficaria assim:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1298959200 -- This is for the Tuesday repeat
2     1             repeat_interval_1  604800
3     1             repeat_start       1299132000 -- This is for the Thursday repeat
4     1             repeat_interval_3  604800
5     2             repeat_start       1299132000
6     2             repeat_interval_5  1          -- Using 1 as a value gives us an event that only happens once

Então, se você tiver um calendário que percorre todos os dias, capturando os eventos do dia em que está, a consulta terá a seguinte aparência:

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
LIMIT 0 , 30

Substituindo {current_timestamp}pelo carimbo de data e hora unix da data atual (menos a hora, os valores de hora, minuto e segundo seriam definidos como 0).

Espero que isso ajude outra pessoa também!


Armazenando padrões de repetição "complexos"

Esse método é mais adequado para armazenar padrões complexos, como

Event A repeats every month on the 3rd of the month starting on March 3, 2011

ou

Event A repeats Friday of the 2nd week of the month starting on March 11, 2011

Eu recomendo combinar isso com o sistema acima para obter mais flexibilidade. As tabelas para isso devem ser como:

ID    NAME
1     Sample Event
2     Another Event

E uma tabela chamada events_metaassim:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1299132000 -- March 3rd, 2011
2     1             repeat_year_1      *
3     1             repeat_month_1     *
4     1             repeat_week_im_1   2
5     1             repeat_weekday_1   6

repeat_week_imrepresenta a semana do mês atual, que pode estar entre 1 e 5 potencialmente. repeat_weekdayno dia da semana, 1-7.

Agora, supondo que você esteja percorrendo os dias / semanas para criar uma exibição mensal em seu calendário, você pode compor uma consulta como esta:

SELECT EV . *
FROM `events` AS EV
JOIN `events_meta` EM1 ON EM1.event_id = EV.id
AND EM1.meta_key = 'repeat_start'
LEFT JOIN `events_meta` EM2 ON EM2.meta_key = CONCAT( 'repeat_year_', EM1.id )
LEFT JOIN `events_meta` EM3 ON EM3.meta_key = CONCAT( 'repeat_month_', EM1.id )
LEFT JOIN `events_meta` EM4 ON EM4.meta_key = CONCAT( 'repeat_week_im_', EM1.id )
LEFT JOIN `events_meta` EM5 ON EM5.meta_key = CONCAT( 'repeat_weekday_', EM1.id )
WHERE (
  EM2.meta_value =2011
  OR EM2.meta_value = '*'
)
AND (
  EM3.meta_value =4
  OR EM3.meta_value = '*'
)
AND (
  EM4.meta_value =2
  OR EM4.meta_value = '*'
)
AND (
  EM5.meta_value =6
  OR EM5.meta_value = '*'
)
AND EM1.meta_value >= {current_timestamp}
LIMIT 0 , 30

Isso combinado com o método acima pode ser combinado para cobrir a maioria dos padrões de eventos repetidos / recorrentes. Se eu perdi alguma coisa, por favor deixe um comentário.


1
Estou tentando o armazenamento de padrões de repetição "simples". se eu precisar repetir toda semana na terça-feira, preciso modificar o repeat_start ou criar um novo registro com a última data. ou existe uma maneira de repetir toda semana com base no primeiro repeat_start ???
loo

1
A sua resposta foi uma grande ajuda @roguecoder mas não deu muito certo fora do bastão ... Eu encontrei a minha resposta depois deste post: stackoverflow.com/questions/10545869/...
Ben Sinclair

1
Em AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1isto é / EM2.meta_valuecolocado incorretamente?
Murali Murugesan

1
Esta é uma grande ajuda. Como você sugeriria acessá-los como registros individuais, por exemplo, se você quisesse fazer comentários ou fazer check-ins em eventos individuais?
johnrees

26
Vale a pena notar que você não deve usar valores codificados para intervalos repetidos, ou seja, 86400segundos em um dia, porque isso não leva em consideração o horário de verão. É mais apropriado calcular essas coisas dinamicamente em tempo real e, em vez disso, armazenar interval = dailye interval_count = 1ou interval = monthlye interval_count = 1.
Corey Ballou

185

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 eventsassim:

ID    NAME
1     Sample Event
2     Another Event

E uma tabela chamada events_metaassim:

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_startser uma data unix de data e hora sem hora (1369008000 corresponde a 20 de maio de 2013) e repeat_intervaluma 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_metaforma:

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_weekdaydados.

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!


6
Isso é incrível, obrigado! Você tem alguma idéia de como codificaria "a cada 2 meses na primeira segunda-feira" ou "a cada 3 meses na primeira segunda-feira" etc.?
Jordan Lev

6
Eu concordo que isso é incrível. No entanto, encontrei o mesmo dilema que Jordan Lev. O campo repeat_interval não é bom para repetir meses porque alguns meses são mais longos que outros. Além disso, como você limita a duração de um evento recorrente. Ou seja, a cada 2 meses na primeira segunda-feira por 8 meses. A tabela deve ter algum tipo de data final.
Abinádi

11
Esta é uma excelente resposta. Abandonei o repeat_interval e adicionei a data repeat_end, mas essa resposta ajudou enormemente.
Iain Collins

3
Dica: Para padrões complexos, é possível eliminar a repeat_intervalcoluna e representá-la nas colunas subseqüentes (ou seja repeat_year, etc.). Para a primeira linha, a situação de repetição toda segunda-feira após 20 de maio de 2013 pode ser representada colocando-se 1 no repeat_weekdaye um *nas outras colunas.
musubi

3
@OlivierMATROT @milos A idéia é definir o campo que você deseja corrigir explicitamente e o restante como curinga *. Portanto, para "todo mês no dia 3", basta definir repeat_day3, o restante dos repeatcampos como * (deixe repeat_intervalnulo) e defina o repeat_start como o código de tempo unix de 3 de março de 2011 para ser sua data de ancoragem.
ahoffner 18/01/19

28

Aprimoramento: substitua o carimbo de data / hora pela data

Como um pequeno aprimoramento da resposta aceita que foi posteriormente refinada pelo ahoffner - é possível usar um formato de data em vez de carimbo de data e hora. As vantagens são:

  1. datas legíveis no banco de dados
  2. nenhum problema com os anos> 2038 e carimbo de data e hora
  3. As remoções precisam ter cuidado com os carimbos de data / hora baseados em datas com ajuste sazonal, ou seja, no Reino Unido, o 28 de junho começa uma hora antes de 28 de dezembro, portanto, derivar um carimbo de data e hora de uma data pode interromper o algoritmo de recursão.

para fazer isso, altere o banco repeat_startde dados para ser armazenado como tipo 'data' e repeat_intervalagora mantenha os dias em vez de segundos. ou seja, 7 por uma repetição de 7 dias.

mude a linha sql:

WHERE (( 1370563200 - repeat_start) % repeat_interval = 0 )

para:

WHERE ( DATEDIFF( '2013-6-7', repeat_start ) % repeat_interval = 0)

Tudo o resto permanece o mesmo. Simples!


E se eu quiser que meu evento se repita ano a ano? repeat_interval deve armazenar 365 dias? E se eles tiverem 366 dias?
precisa saber é o seguinte

3
@ George02 se o evento for anual, você deixa repeat_interval NULL e repeat_year é *, dependendo da recorrência, você pode definir repeat_month e repeat_day, por exemplo, 11 de março ou repeat_month, repeat_week e repeat_weekday para definir a segunda terça-feira de abril.
Jerrygarciuh 8/15

25

Eu seguiria este guia: https://github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

Além disso, certifique-se de usar o formato iCal para não reinventar a roda e lembre-se da Regra # 0: NÃO armazene instâncias de eventos recorrentes individuais como linhas no seu banco de dados!


2
Como você modelaria os usuários de rastreamento que participaram de uma instância específica? Faz sentido quebrar a Regra # 0 neste caso?
Danny Sullivan

2
@DannySullivan Do alto da minha cabeça, eu teria outra entidade attendedEventcom baseInstanceIde instanceStartDate- Esse é, por exemplo, o evento base a partir do qual você criou a exibição do calendário de regras recorrentes e usou a data de início para especificar informações sobre essa instância específica. Essa entidade também pode tem algo parecido com o attendedListIdque leva a outra tabela de id,attendedUserId
Gal Bracha 26/09

@DannySullivan Eu sei que faz um tempo desde que você perguntou. Porém, fora do comentário anterior, você sempre pode fazer uma pesquisa inversa para ver se esse usuário fazia parte do padrão de recorrência de eventos. Isso diria se eles estavam pelo menos agendados para o evento. Se eles realmente compareceram ou não, é uma história diferente, que seria mais parecida com o comentário de DannySullivan.
BRogers 23/03

24

Para todos que estão interessados ​​nisso, agora você pode simplesmente copiar e colar para começar em minutos. Eu segui o conselho nos comentários o melhor que pude. Deixe-me saber se estou faltando alguma coisa.

"VERSÃO COMPLEXA":

eventos

+ ---------- + ---------------- +
| ID NOME
+ ---------- + ---------------- +
| 1 | Evento de amostra 1 |
| 2 Segundo evento |
| 3 Terceiro evento |
+ ---------- + ---------------- +

events_meta

+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| ID event_id | repeat_start | repeat_interval | repeat_year | repeat_month | repeat_day | repeat_week | repeat_weekday |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| 1 | 1 | 04/07/2014 7 NULL NULL NULL NULL NULL
| 2 2 26/06/2014 | NULL 2014 * * 2 5
| 3 3 04/07/2014 NULL * * * * 5
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +

Código SQL:

CREATE TABLE IF NOT EXISTS `events` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;

--
-- Dumping data for table `events`
--

INSERT INTO `events` (`ID`, `NAME`) VALUES
(1, 'Sample event'),
(2, 'Another event'),
(3, 'Third event...');

CREATE TABLE IF NOT EXISTS `events_meta` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `event_id` int(11) NOT NULL,
  `repeat_start` date NOT NULL,
  `repeat_interval` varchar(255) NOT NULL,
  `repeat_year` varchar(255) NOT NULL,
  `repeat_month` varchar(255) NOT NULL,
  `repeat_day` varchar(255) NOT NULL,
  `repeat_week` varchar(255) NOT NULL,
  `repeat_weekday` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `ID` (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

--
-- Dumping data for table `events_meta`
--

INSERT INTO `events_meta` (`ID`, `event_id`, `repeat_start`, `repeat_interval`, `repeat_year`, `repeat_month`, `repeat_day`, `repeat_week`, `repeat_weekday`) VALUES
(1, 1, '2014-07-04', '7', 'NULL', 'NULL', 'NULL', 'NULL', 'NULL'),
(2, 2, '2014-06-26', 'NULL', '2014', '*', '*', '2', '5'),
(3, 3, '2014-07-04', 'NULL', '*', '*', '*', '*', '1');

também disponível como exportação MySQL (para fácil acesso)

Exemplo de código PHP index.php:

<?php
    require 'connect.php';    

    $now = strtotime("yesterday");

    $pushToFirst = -11;
    for($i = $pushToFirst; $i < $pushToFirst+30; $i++)
    {
        $now = strtotime("+".$i." day");
        $year = date("Y", $now);
        $month = date("m", $now);
        $day = date("d", $now);
        $nowString = $year . "-" . $month . "-" . $day;
        $week = (int) ((date('d', $now) - 1) / 7) + 1;
        $weekday = date("N", $now);

        echo $nowString . "<br />";
        echo $week . " " . $weekday . "<br />";



        $sql = "SELECT EV.*
                FROM `events` EV
                RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
                WHERE ( DATEDIFF( '$nowString', repeat_start ) % repeat_interval = 0 )
                OR ( 
                    (repeat_year = $year OR repeat_year = '*' )
                    AND
                    (repeat_month = $month OR repeat_month = '*' )
                    AND
                    (repeat_day = $day OR repeat_day = '*' )
                    AND
                    (repeat_week = $week OR repeat_week = '*' )
                    AND
                    (repeat_weekday = $weekday OR repeat_weekday = '*' )
                    AND repeat_start <= DATE('$nowString')
                )";
        foreach ($dbConnect->query($sql) as $row) {
            print $row['ID'] . "\t";
            print $row['NAME'] . "<br />";
        }

        echo "<br /><br /><br />";
    }
?>

Exemplo de código PHP connect.php:

<?
// ----------------------------------------------------------------------------------------------------
//                                       Connecting to database
// ----------------------------------------------------------------------------------------------------
// Database variables
$username = "";
$password = "";
$hostname = ""; 
$database = ""; 

// Try to connect to database and set charset to UTF8
try {
    $dbConnect = new PDO("mysql:host=$hostname;dbname=$database;charset=utf8", $username, $password);
    $dbConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

} catch(PDOException $e) {
    echo 'ERROR: ' . $e->getMessage();
}
// ----------------------------------------------------------------------------------------------------
//                                      / Connecting to database
// ----------------------------------------------------------------------------------------------------
?>

Além disso, o código php está disponível aqui (para melhor legibilidade):
index.php
e
connect.php
Agora, configurar isso deve levar alguns minutos. Não horas. :)


2
como posso consultar para obter todos os eventos repetidos em um período ... ou seja, obter todos os eventos repetidos entre 01/10/2014 a 30/12/2014. obrigado pela sua publicação
Well Wisher

@Wellwisher - repita ... até e tabela temporária stackoverflow.com/questions/34407833/…
Brad Kent

1
@ Alex Como posso excluir uma ocorrência de instância de um evento repetido.
Pugazhenthi

1
Eu sei que este é um segmento antigo, mas por que o tipo varchar nas colunas repeat_ *? Não foi possível usar um número inteiro e um valor negativo em vez de '*'?
Olivier MATROT

1
Obrigado pelo código. Devo, no entanto, observar que sua implementação de banco de dados / consultas é um pouco perturbadora e muito ineficiente. Por exemplo, por que usar varchar (255) para colunas tão simples (como @OlivierMATROT mencionado, você pode usar números inteiros e, se não, por que 255?). E se você estiver repetindo a consulta 30 vezes, por que não usar instruções ou procedimentos? Só estou dizendo se alguém está prestes a implementar isso.
Rony

15

Enquanto as soluções propostas funcionavam, eu estava tentando implementar com o Calendário completo e exigiria mais de 90 chamadas de banco de dados para cada visualização (como carrega o mês atual, o anterior e o próximo mês), das quais eu não estava muito empolgado.

Encontrei uma biblioteca de recursão https://github.com/tplaner/Quando você simplesmente armazena as regras no banco de dados e uma consulta para obter todas as regras relevantes.

Espero que isso ajude outra pessoa, pois passei muitas horas tentando encontrar uma boa solução.

Edit: Esta biblioteca é para PHP


Eu também quero usar o fullcalendar. Como quando a biblioteca pode me ajudar? Como extrair eventos propoer?
piernik 11/11

@piernik - eu configuraria a biblioteca como na documentação e, se você estiver enfrentando problemas específicos, abra uma nova pergunta no stackoverflow com o código que você configurou e os problemas que está tendo. Tenho certeza de que, se você colocar tanto esforço em alguns membros, irá ajudá-lo.
Tim Ramsey #

Quero dizer que usar WhenVocê tem que armazenar todas as datas recorrentes no banco de dados ou obter todos os eventos recorrentes e gerar datas no php no no banco de dados. Estou certo?
Piernik

@piernik Você armazenaria a Data e as regras iniciais no banco de dados e usaria Whenpara gerar todas as datas - que são preenchidas a partir das datas / regras armazenadas inicialmente.
Tim Ramsey

Também não é bom - você não pode em um único comando mysql obter eventos corretos - Você precisa usar o PHP para isso. Obrigado de qualquer maneira
piernik 15/10

14

Por que não usar um mecanismo semelhante aos trabalhos do cron Apache? http://en.wikipedia.org/wiki/Cron

Para calendar \ scheduling, eu usaria valores ligeiramente diferentes para "bits" para acomodar eventos padrão de recorrência do calendário - em vez de [dia da semana (0 a 7), mês (1 a 12), dia do mês (1 a 31), hora (0 - 23), min (0 - 59)]

- Eu usaria algo como [Ano (repita a cada N anos), mês (1-12), dia do mês (1-31), semana do mês (1-5), dia da semana (0 - 7) ]

Espero que isto ajude.


6
Eu acho que são muitas opções de dia da semana. 1-7 ou 0-6 parecem mais precisos.
Abinadi

2
É bom usar o cron para armazenar a repetição. mas o problema é que é muito difícil procurar.
Stony

@Vladimir como você armazena, a cada dois terça-feira (a cada duas semanas na terça-feira)
julestruong


O cron possui expressividade limitada, uma vez que é apátrida (apenas comparando data / hora atual / hipotérmica a um padrão); portanto, não pode representar certos padrões comerciais / humanos comuns, como "a cada terceiro dia" ou "a cada 7 horas", o que requer lembrar a última ocorrência. Isto não é óbvio; você pode pensar que acabou de dizer dia / 3 ou hora / 7 no crontab, mas, no final do mês / dia, tem dias / horas "restantes" que são inferiores a 3 ou 7; com possíveis resultados catastróficos.
Jaime Guerrero

5

Eu desenvolvi uma linguagem de programação esotérica apenas para este caso. A melhor parte disso é que ele é menos esquema e independente de plataforma. Você apenas precisa escrever um programa seletor, para sua programação, cuja sintaxe é restrita pelo conjunto de regras descritas aqui -

https://github.com/tusharmath/sheql/wiki/Rules

As regras são extensíveis e você pode adicionar qualquer tipo de personalização com base no tipo de lógica de repetição que deseja executar, sem se preocupar com migrações de esquema etc.

Essa é uma abordagem completamente diferente e pode ter algumas desvantagens.


4

Parece muito com eventos do MySQL que são armazenados nas tabelas do sistema. Você pode olhar para a estrutura e descobrir quais colunas não são necessárias:

   EVENT_CATALOG: NULL
    EVENT_SCHEMA: myschema
      EVENT_NAME: e_store_ts
         DEFINER: jon@ghidora
      EVENT_BODY: SQL
EVENT_DEFINITION: INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP())
      EVENT_TYPE: RECURRING
      EXECUTE_AT: NULL
  INTERVAL_VALUE: 5
  INTERVAL_FIELD: SECOND
        SQL_MODE: NULL
          STARTS: 0000-00-00 00:00:00
            ENDS: 0000-00-00 00:00:00
          STATUS: ENABLED
   ON_COMPLETION: NOT PRESERVE
         CREATED: 2006-02-09 22:36:06
    LAST_ALTERED: 2006-02-09 22:36:06
   LAST_EXECUTED: NULL
   EVENT_COMMENT:


3

@Rogue Coder

Isso é ótimo!

Você pode simplesmente usar a operação modulo (MOD ou% no mysql) para simplificar seu código no final:

Ao invés de:

AND (
    ( CASE ( 1299132000 - EM1.`meta_value` )
        WHEN 0
          THEN 1
        ELSE ( 1299132000 - EM1.`meta_value` )
      END
    ) / EM2.`meta_value`
) = 1

Faz:

$current_timestamp = 1299132000 ;

AND ( ('$current_timestamp' - EM1.`meta_value` ) MOD EM2.`meta_value`) = 1")

Para levar isso adiante, pode-se incluir eventos que não se repetem para sempre.

Algo como "repeat_interval_1_end" para indicar a data do último "repeat_interval_1" pode ser adicionado. No entanto, isso torna a consulta mais complicada e eu realmente não consigo descobrir como fazer isso ...

Talvez alguém possa ajudar!


1

Os dois exemplos que você deu são muito simples; eles podem ser representados como um intervalo simples (o primeiro sendo quatro dias, o segundo sendo 14 dias). Como você modela isso dependerá inteiramente da complexidade de suas recorrências. Se o que você tem acima é realmente simples, armazene uma data de início e o número de dias no intervalo de repetição.

Se, no entanto, você precisar apoiar coisas como

O evento A se repete todos os meses no dia 3 de março, com início em 3 de março de 2011

Ou

O evento A se repete na segunda sexta-feira do mês, a partir de 11 de março de 2011

Então esse é um padrão muito mais complexo.


1
Eu adicionei as regras mais complexas que você acabou de declarar mais tarde, mas não por enquanto. Como eu modelaria a consulta SQL para obter eventos, por exemplo, 7 de março de 2011, para que ele recebesse meu evento recorrente?
Brandon Wamboldt
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.