Calendario de eventos recurrentes / repetitivos: el mejor método de almacenamiento

311

Estoy creando un sistema de eventos personalizado, y si tiene un evento repetitivo que se ve así:

El evento A se repite cada 4 días a partir del 3 de marzo de 2011.

o

El evento B se repite cada 2 semanas el martes a partir del 1 de marzo de 2011

¿Cómo puedo almacenar eso en una base de datos de manera que sea fácil de buscar? No quiero problemas de rendimiento si hay una gran cantidad de eventos, y tengo que revisar todos y cada uno al representar el calendario.

Brandon Wamboldt
fuente
¿Puedes explicar por qué 1299132000 está codificado? ¿Qué hará esto si necesito obtener las fechas de ocurrencia y el usuario para la fecha de finalización dada?
Murali Murugesan
@Murali Boy esto es viejo, pero estoy bastante seguro de que 1299132000 es la fecha actual.
Brandon Wamboldt
@BrandonWamboldt, probé tu idea con SQL Server. stackoverflow.com/questions/20286332/display-next-event-date . Quiero encontrar todos los siguientes elementos, como la versión de C #
Billa

Respuestas:

211

Almacenar patrones repetitivos "simples"

Para mi calendario basado en PHP / MySQL, quería almacenar información de eventos recurrentes / recurrentes de la manera más eficiente posible. No quería tener una gran cantidad de filas, y quería buscar fácilmente todos los eventos que tendrían lugar en una fecha específica.

El siguiente método es excelente para almacenar información repetitiva que ocurre a intervalos regulares, como todos los días, cada n días, todas las semanas, todos los meses, etc., etc. Esto también incluye todos los patrones de tipo martes y jueves, porque se almacenan por separado, ya que cada semana comienza un martes y cada semana comienza un jueves.

Suponiendo que tengo dos tablas, una llamada eventsasí:

ID    NAME
1     Sample Event
2     Another Event

Y una mesa llamada events_metaasí:

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

Dado que repeat_start es una fecha sin tiempo como marca de tiempo unix, y repeat_interval una cantidad en segundos entre intervalos (432000 son 5 días).

repeat_interval_1 va con repeat_start de la ID 1. Entonces, si tengo un evento que se repite todos los martes y todos los jueves, el repeat_interval sería 604800 (7 días), y habría 2 repeat_starts y 2 repeat_intervals. La tabla se vería así:

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

Luego, si tiene un calendario que recorre todos los días, capturando los eventos para el día en que son, la consulta se vería así:

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

Reemplazar {current_timestamp}con la marca de tiempo de Unix para la fecha actual (menos la hora, por lo que los valores de hora, minuto y segundo se establecerían en 0).

¡Espero que esto ayude a alguien más también!


Almacenamiento de patrones repetitivos "complejos"

Este método es más adecuado para almacenar patrones complejos como

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

o

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

Recomiendo combinar esto con el sistema anterior para obtener la mayor flexibilidad. Las tablas para esto deberían gustar:

ID    NAME
1     Sample Event
2     Another Event

Y una mesa llamada events_metaasí:

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 la semana del mes actual, que podría estar entre 1 y 5 potencialmente. repeat_weekdayen el día de la semana, 1-7.

Ahora, suponiendo que esté recorriendo los días / semanas para crear una vista mensual en su calendario, puede redactar una 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

Esto combinado con el método anterior podría combinarse para cubrir la mayoría de los patrones de eventos recurrentes / recurrentes. Si me he perdido algo, por favor deje un comentario.

Brandon Wamboldt
fuente
1
Estoy tratando de almacenar patrones repetitivos "simples". si necesito que se repita todas las semanas el martes, ¿tengo que modificar repeat_start o crear un nuevo registro con la última fecha? ¿o hay una manera de que se repita todas las semanas en función del primer repeat_start ??
Loo
1
Su respuesta fue de gran ayuda @roguecoder pero no funcionó del todo ... Encontré mi respuesta después de esta publicación: stackoverflow.com/questions/10545869/…
Ben Sinclair
1
¿En AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1esto se / EM2.meta_valuecoloca mal?
Murali Murugesan
1
Esta es una gran ayuda. ¿Cómo sugeriría acceder a estos como registros individuales, por ejemplo, si desea tener comentarios o verificaciones sobre eventos individuales?
johnrees
26
Vale la pena señalar que no debe usar valores codificados para intervalos de repetición, es decir, 86400segundos en un día, porque no tiene en cuenta el horario de verano. Es más apropiado calcular estas cosas dinámicamente sobre la marcha y en su lugar almacenar interval = dailyy interval_count = 1o interval = monthlyy interval_count = 1.
Corey Ballou
185

Si bien la respuesta actualmente aceptada fue de gran ayuda para mí, quería compartir algunas modificaciones útiles que simplifican las consultas y también aumentan el rendimiento.


Eventos de repetición "simples"

Para manejar eventos que se repiten a intervalos regulares, como:

Repeat every other day 

o

Repeat every week on Tuesday 

Debería crear dos tablas, una llamada eventsasí:

ID    NAME
1     Sample Event
2     Another Event

Y una mesa llamada events_metaasí:

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

Al repeat_startser una fecha de marca de tiempo de Unix sin tiempo (1369008000 corresponde al 20 de mayo de 2013), y repeat_intervaluna cantidad en segundos entre intervalos (604800 es de 7 días).

Al recorrer cada día en el calendario, puede obtener eventos repetidos utilizando esta simple consulta:

SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE  (( 1299736800 - repeat_start) % repeat_interval = 0 )

Simplemente sustituya en la marca de tiempo Unix (1299736800) para cada fecha en su calendario.

Tenga en cuenta el uso del módulo (signo%). Este símbolo es como una división regular, pero devuelve el '' resto '' en lugar del cociente, y como tal es 0 siempre que la fecha actual sea un múltiplo exacto del repetido_intervalo desde el repetido_inicio.

Comparación de rendimiento

Esto es significativamente más rápido que la respuesta basada en "meta_keys" sugerida anteriormente, que era la siguiente:

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 ejecuta EXPLAIN esta consulta, notará que requiere el uso de un búfer de unión:

+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 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 solución con 1 combinación anterior no requiere tal búfer.


Patrones "complejos"

Puede agregar compatibilidad con tipos más complejos para admitir estos tipos de reglas de repetición:

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

o

Event A repeats second Friday of the month starting on March 11, 2011

Su tabla de eventos puede verse exactamente igual:

ID    NAME
1     Sample Event
2     Another Event

Luego, para agregar soporte para estas reglas complejas, agregue columnas para que les events_metaguste:

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    

Tenga en cuenta que sólo hay que especificar una repeat_interval o un conjunto de repeat_year, repeat_month, repeat_day, repeat_week, y repeat_weekdaydatos.

Esto hace que la selección de ambos tipos simultáneamente sea muy simple. Simplemente recorra cada día y complete los valores correctos (1370563200 para el 7 de junio de 2013, y luego el año, mes, día, número de semana y día de la semana de la siguiente manera):

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
  )

Esto devuelve todos los eventos que se repiten el viernes de la segunda semana, así como todos los eventos que se repiten todos los viernes, por lo que devuelve los ID de evento 1 y 2:

ID    NAME
1     Sample Event
2     Another Event

* Nota al margen en el SQL anterior. Utilicé los índices de lunes a viernes predeterminados de PHP Date , así que "5" para el viernes


¡Espero que esto ayude a otros tanto como la respuesta original me ayudó!

ahoffner
fuente
66
Esto es asombroso, gracias! ¿Tiene alguna idea de cómo codificaría "cada 2 meses el primer lunes" o "cada 3 meses el primer lunes", etc.?
Jordan Lev
66
Estoy de acuerdo en que esto es asombroso. Sin embargo, me encontré con el mismo dilema que Jordan Lev. El campo repeat_interval no sirve para repetir meses porque algunos meses son más largos que otros. Además, ¿cómo se limita la duración de un evento recurrente? Es decir, cada 2 meses el primer lunes durante 8 meses. La tabla debe tener algún tipo de fecha de finalización.
Abinadi el
11
Esta es una excelente respuesta. Dejé caer el número de repetición y agregué la fecha de repetición, pero esta respuesta me ayudó enormemente.
Iain Collins el
3
Consejo: Para patrones complejos, se podría eliminar la repeat_intervalcolumna y representarla en las columnas posteriores (es decir repeat_year, etc.). Para la primera fila, la situación de repetición todos los lunes después del 20 de mayo de 2013, se puede representar colocando un 1 en el repeat_weekdayy una *en las otras columnas.
musubi
3
@OlivierMATROT @milos La idea es establecer el campo que desea que se repare explícitamente, y el resto al comodín *. Por lo tanto, para "cada mes el día 3" simplemente establece repeat_dayen 3, el resto de los repeatcampos en * (dejar repeat_intervalnulo), y establece el valor de repeat_start en el código de tiempo de Unix para el 3 de marzo de 2011 como su fecha de anclaje.
ahoffner
28

Mejora: reemplace la marca de tiempo con la fecha

Como una pequeña mejora a la respuesta aceptada que posteriormente fue refinada por ahoffner, es posible utilizar un formato de fecha en lugar de una marca de tiempo. Las ventajas son:

  1. fechas legibles en la base de datos
  2. no hay problema con los años> 2038 y la marca de tiempo
  3. Las eliminaciones deben tener cuidado con las marcas de tiempo que se basan en fechas ajustadas estacionalmente, es decir, en el Reino Unido, el 28 de junio comienza una hora antes del 28 de diciembre, por lo que derivar una marca de tiempo de una fecha puede romper el algoritmo de recursión.

para hacer esto, cambie la base de datos repeat_startpara que se almacene como tipo 'fecha' y repeat_intervalahora contenga días en lugar de segundos. es decir, 7 para una repetición de 7 días.

cambiar la línea sql:

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

a:

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

Todo lo demás se mantiene igual. Simples!

usuario3781087
fuente
Entonces, ¿qué pasa si quiero que mi evento se repita año tras año? repetir_intervalo debe almacenar 365 días? ¿Qué pasa si el año tiene 366 días?
TGeorge
3
@ George02 si el evento es anual, deja repite_interval NULL y repeat_year es *, luego, dependiendo de la recurrencia, puede configurar repeat_month y repeat_day, por ejemplo, el 11 de marzo o repeat_month, repeat_week y repeat_weekday para configurar el segundo martes de abril.
jerrygarciuh
25

Seguiría esta guía: https://github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

También asegúrese de usar el formato iCal para no reinventar la rueda y recuerde la Regla # 0: ¡NO almacene instancias de eventos recurrentes individuales como filas en su base de datos!

Gal Bracha
fuente
2
¿Cómo modelaría un seguimiento de usuarios que han asistido a una instancia específica? ¿Tiene sentido romper con la Regla # 0 en este caso?
Danny Sullivan
2
@DannySullivan Desde lo alto de mi cabeza, tendría otra entidad attendedEventcon baseInstanceIdy instanceStartDate- Ese es, por ejemplo, el evento base a partir del cual creó la vista de calendario de reglas recurrentes y use la fecha de inicio para especificar información sobre esa instancia específica. Entonces esta entidad también podría tener algo como attendedListIdlo que conduce a otra mesa de id,attendedUserId
Gal Bracha
@DannySullivan Sé que ha pasado un tiempo desde que lo preguntaste. Pero fuera del comentario anterior, siempre podría hacer una búsqueda inversa para ver si ese usuario era parte del patrón de recurrencia del evento. Eso le diría si al menos estaban programados para el evento. Si realmente asistieron o no, es una historia diferente que estaría más en la línea del comentario de DannySullivan.
Brogers
24

Para todos los que estén interesados ​​en esto, ahora pueden simplemente copiar y pegar para comenzar en cuestión de minutos. Tomé el consejo en los comentarios lo mejor que pude. Avísame si me falta algo.

"VERSIÓN COMPLEJA":

eventos

+ ---------- + ---------------- +
El | ID | NOMBRE |
+ ---------- + ---------------- +
El | 1 | Ejemplo de evento 1 |
El | 2 | Segundo evento |
El | 3 | Tercer evento |
+ ---------- + ---------------- +

eventos_meta

+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
El | ID | event_id | repetir_inicio | repetir_intervalo | repetir_año | repetir_mes | repetir_día | repetir_semana | repetir_weekday |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
El | 1 | 1 | 2014-07-04 | 7 | NULL | NULL | NULL | NULL | NULL |
El | 2 | 2 | 2014-06-26 | NULL | 2014 | * | * | 2 | 5 |
El | 3 | 3 | 2014-07-04 | 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');

también disponible como exportación MySQL (para fácil acceso)

Código de ejemplo 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 />";
    }
?>

Código de ejemplo 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
// ----------------------------------------------------------------------------------------------------
?>

También el código php está disponible aquí (para una mejor legibilidad):
index.php
y
connect.php.
Ahora configurar esto debería llevarle unos minutos. No horas :)

Alex
fuente
2
¿Cómo puedo consultar para obtener todos los eventos repetidos dentro de un rango de fechas ... es decir, obtener todos los eventos repetidos entre 2014-10-01 a 2014-12-30. gracias por tu publicación
Well Wisher
@Wellwisher - repita ... hasta y tabla temporal stackoverflow.com/questions/34407833/…
Brad Kent
1
@ Alex ¿Cómo puedo eliminar una instancia de evento repetido?
Pugazhenthi
1
Sé que este es un hilo antiguo, pero ¿por qué el tipo varchar en las columnas repeat_ *? ¿No podría usar un número entero y un valor negativo en lugar de '*'?
Olivier MATROT
1
Gracias por el codigo. Sin embargo, debo comentar que su implementación de db / queries es un poco inquietante y muy ineficiente. Por ejemplo, por qué usar varchar (255) para columnas tan simples (como mencionó @OlivierMATROT, podría usar números enteros, e incluso si no, ¿por qué 255?). Y si está repitiendo la consulta 30 veces, ¿por qué no usar declaraciones o procedimientos? Solo digo por el bien si alguien está a punto de implementar esto.
Rony
15

Si bien las soluciones propuestas funcionan, estaba tratando de implementarlas con Full Calendar y requeriría más de 90 llamadas a la base de datos para cada vista (ya que carga el mes actual, anterior y siguiente), lo cual no me entusiasmó demasiado.

Encontré una biblioteca de recursión https://github.com/tplaner/Cuando simplemente almacena las reglas en la base de datos y una consulta para extraer todas las reglas relevantes.

Espero que esto ayude a alguien más, ya que pasé tantas horas tratando de encontrar una buena solución.

Editar: esta biblioteca es para PHP

Tim Ramsey
fuente
También quiero usar fullcalendar. ¿Cómo cuando la biblioteca podría ayudarme? ¿Cómo extraer eventos propoer?
piernik
@piernik: configuraría la biblioteca como en la documentación y, si tiene problemas específicos, abra una nueva pregunta sobre stackoverflow con el código que ha configurado y los problemas que tiene. Estoy seguro de que si pones tanto esfuerzo en algunos miembros te ayudarán.
Tim Ramsey
Quiero decir que usar WhenTienes que almacenar todas las fechas recurrentes en la base de datos u obtener todos los eventos recurrentes y generar fechas en php no en la base de datos. Estoy en lo cierto?
piernik
@piernik Debería almacenar la Fecha inicial y la / s regla / s en la base de datos y utilizarla Whenpara generar todas las fechas, que se rellenan a partir de la fecha / reglas almacenadas iniciales.
Tim Ramsey
Tampoco es bueno: no puede en un solo comando mysql obtener eventos correctos, debe usar PHP para eso. Gracias de todos modos
piernik
14

¿Por qué no utilizar un mecanismo similar a los trabajos de Apache cron? http://en.wikipedia.org/wiki/Cron

Para la programación de calendario, usaría valores ligeramente diferentes para "bits" para acomodar eventos de reoccurence de calendario estándar, en lugar de [día de la semana (0 - 7), mes (1 - 12), día del mes (1 - 31), hora (0 - 23), min (0 - 59)]

- Usaría algo como [Año (repetir cada N años), mes (1 - 12), día del mes (1 - 31), semana del mes (1-5), día de la semana (0 - 7) ]

Espero que esto ayude.

Vladimir
fuente
66
Creo que son demasiadas opciones de día de la semana. Ya sea 1-7 o 0-6 parece más preciso.
Abinadi el
2
Es bueno usar cron para almacenar la repetición. pero el problema es que es muy difícil de buscar.
Stony
@Vladimir, ¿cómo almacenas, cada dos martes (cada dos semanas el martes)
julestruong
@julestruong parece que este sitio web tiene la respuesta: coderwall.com/p/yzzu5a/running-a-cron-job-every-other-week
Ashton Wiersdorf
cron tiene una expresividad limitada, ya que no tiene estado (simplemente compara la fecha / hora actual / hipotética con un patrón), como tal, no puede representar ciertos patrones comerciales / humanos comunes como "cada tercer día" o "cada 7 horas", que requieren recordar la última ocurrencia. Esto no es obvio; puede pensar que solo dice día / 3 u hora / 7 en crontab, pero luego al final del mes / día, tiene "días / horas sobrantes" que son menos de 3 o 7; con posibles resultados catastróficos.
Jaime Guerrero
5

Desarrollé un lenguaje de programación esotérico solo para este caso. La mejor parte es que no tiene esquema y es independiente de la plataforma. Solo tiene que escribir un programa selector, para su programación, cuya sintaxis está limitada por el conjunto de reglas que se describen aquí:

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

Las reglas son extensibles y puede agregar cualquier tipo de personalización según el tipo de lógica de repetición que desee realizar, sin preocuparse por las migraciones de esquemas, etc.

Este es un enfoque completamente diferente y puede tener algunas desventajas propias.

Tusharmath
fuente
4

Suena muy parecido a los eventos MySQL que se almacenan en las tablas del sistema. Puede mirar la estructura y descubrir qué columnas no son necesarias:

   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:
Valentin Kuzub
fuente
3

@Rogue Coder

¡Esto es genial!

Simplemente puede usar la operación de módulo (MOD o% en mysql) para simplificar su código al final:

En vez de:

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

Hacer:

$current_timestamp = 1299132000 ;

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

Para llevar esto más lejos, uno podría incluir eventos que no se repiten para siempre.

Se podría agregar algo como "repeat_interval_1_end" para indicar la fecha de la última "repeat_interval_1". Sin embargo, esto hace que la consulta sea más complicada y realmente no puedo entender cómo hacer esto ...

¡Quizás alguien podría ayudar!

dorogz
fuente
1

Los dos ejemplos que has dado son muy simples; se pueden representar como un intervalo simple (el primero son cuatro días, el segundo son 14 días). La forma en que modele esto dependerá completamente de la complejidad de sus recurrencias. Si lo que tiene arriba es realmente así de simple, almacene una fecha de inicio y la cantidad de días en el intervalo de repetición.

Sin embargo, si necesita apoyar cosas como

El evento A se repite todos los meses el 3 de cada mes a partir del 3 de marzo de 2011.

O

El evento A se repite el segundo viernes del mes a partir del 11 de marzo de 2011

Entonces ese es un patrón mucho más complejo.

Adam Robinson
fuente
1
Hice agregar las reglas más complejas que acaba de establecer en un momento posterior, pero no por ahora. ¿Cómo modelaría la consulta SQL para obtener eventos el 7 de marzo de 2011 para obtener mi evento recurrente?
Brandon Wamboldt