Wiederkehrende / wiederholte Ereignisse im Kalender - Beste Speichermethode

311

Ich baue ein benutzerdefiniertes Ereignissystem auf, und wenn Sie ein sich wiederholendes Ereignis haben, das so aussieht:

Ereignis A wird ab dem 3. März 2011 alle 4 Tage wiederholt

oder

Event B wird ab dem 1. März 2011 alle 2 Wochen am Dienstag wiederholt

Wie kann ich das in einer Datenbank so speichern, dass die Suche einfacher wird? Ich möchte keine Leistungsprobleme, wenn es eine große Anzahl von Ereignissen gibt, und ich muss jedes einzelne beim Rendern des Kalenders durchgehen.

Brandon Wamboldt
quelle
Können Sie erklären, warum fest 1299132000 codiert ist? Was wird dies tun, wenn ich die Vorkommensdaten und den Benutzer für das angegebene Enddatum erhalten muss?
Murali Murugesan
@Murali Boy das ist alt, aber ich bin mir ziemlich sicher, dass 1299132000 das aktuelle Datum sein soll.
Brandon Wamboldt
@BrandonWamboldt, ich habe Ihre Idee mit SQL Server ausprobiert. stackoverflow.com/questions/20286332/display-next-event-date . Ich möchte alle nächsten Elemente wie c # Version finden
Billa

Antworten:

211

Speichern von "einfachen" sich wiederholenden Mustern

Für meinen PHP / MySQL-basierten Kalender wollte ich Informationen zu sich wiederholenden / wiederkehrenden Ereignissen so effizient wie möglich speichern. Ich wollte nicht viele Zeilen haben und ich wollte einfach alle Ereignisse nachschlagen, die an einem bestimmten Datum stattfinden würden.

Die folgende Methode eignet sich hervorragend zum Speichern sich wiederholender Informationen, die in regelmäßigen Abständen auftreten, z. B. jeden Tag, alle n Tage, jede Woche, jeden Monat, jedes Jahr usw. usw. Dies schließt auch Muster vom Typ Dienstag und Donnerstag ein, da sie gespeichert werden getrennt wie jede Woche ab Dienstag und jede Woche ab Donnerstag.

Angenommen, ich habe zwei Tabellen, von denen eine so heißt events:

ID    NAME
1     Sample Event
2     Another Event

Und ein Tisch namens events_metaso:

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

Bei repeat_start handelt es sich um ein Datum ohne Uhrzeit als Unix-Zeitstempel, und bei repeat_interval um einen Betrag in Sekunden zwischen den Intervallen (432000 beträgt 5 Tage).

repeat_interval_1 gehört zu repeat_start der ID 1. Wenn ich also ein Ereignis habe, das sich jeden Dienstag und jeden Donnerstag wiederholt, wäre das repeat_interval 604800 (7 Tage) und es gäbe 2 repeat_starts und 2 repeat_intervals. Der Tisch würde so aussehen:

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

Wenn Sie dann einen Kalender haben, der jeden Tag durchläuft und die Ereignisse für den Tag abruft, an dem er sich befindet, sieht die Abfrage folgendermaßen aus:

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

Ersetzen {current_timestamp}durch den Unix-Zeitstempel für das aktuelle Datum (abzüglich der Uhrzeit, sodass die Werte für Stunde, Minute und Sekunde auf 0 gesetzt werden).

Hoffentlich hilft das auch jemand anderem!


Speichern "komplexer" sich wiederholender Muster

Diese Methode eignet sich besser zum Speichern komplexer Muster wie z

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

oder

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

Ich würde empfehlen, dies mit dem oben genannten System zu kombinieren, um die größtmögliche Flexibilität zu erzielen. Die Tabellen dafür sollten wie folgt aussehen:

ID    NAME
1     Sample Event
2     Another Event

Und ein Tisch namens events_metaso:

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_imstellt die Woche des aktuellen Monats dar, die möglicherweise zwischen 1 und 5 liegen kann. repeat_weekdayam Wochentag 1-7.

Angenommen, Sie durchlaufen die Tage / Wochen, um eine Monatsansicht in Ihrem Kalender zu erstellen, können Sie eine Abfrage wie folgt erstellen:

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

Dies kann in Kombination mit dem obigen Verfahren kombiniert werden, um die meisten sich wiederholenden / wiederkehrenden Ereignismuster abzudecken. Wenn ich etwas verpasst habe, hinterlasse bitte einen Kommentar.

Brandon Wamboldt
quelle
1
Ich versuche, Ihre "einfachen" sich wiederholenden Muster zu speichern. Wenn ich es jede Woche am Dienstag wiederholen muss, muss ich den repeat_start ändern oder einen neuen Datensatz mit dem letzten Datum erstellen. oder gibt es eine möglichkeit, es jede woche basierend auf dem ersten repeat_start zu wiederholen ???
Klo
1
Ihre Antwort war eine große Hilfe @roguecoder, aber es hat nicht ganz geklappt ... Ich fand meine Antwort nach diesem Beitrag: stackoverflow.com/questions/10545869/…
Ben Sinclair
1
In AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1ist / EM2.meta_valuedas falsch platziert?
Murali Murugesan
1
Das ist eine große Hilfe. Wie würden Sie vorschlagen, auf diese als einzelne Datensätze zuzugreifen, wenn Sie beispielsweise Kommentare oder Check-ins zu einzelnen Ereignissen wünschen?
Johnrees
26
Es ist erwähnenswert, dass Sie keine fest codierten Werte für Wiederholungsintervalle verwenden sollten, dh 86400Sekunden an einem Tag, da die Sommerzeit nicht berücksichtigt wird. Es ist besser, diese Dinge dynamisch im laufenden Betrieb zu berechnen und stattdessen interval = dailyund interval_count = 1oder interval = monthlyund zu speichern interval_count = 1.
Corey Ballou
185

Während die derzeit akzeptierte Antwort eine große Hilfe für mich war, wollte ich einige nützliche Änderungen mitteilen, die die Abfragen vereinfachen und auch die Leistung steigern.


"Einfache" Wiederholungsereignisse

So behandeln Sie Ereignisse, die in regelmäßigen Abständen wiederholt werden, wie z.

Repeat every other day 

oder

Repeat every week on Tuesday 

Sie sollten zwei Tabellen erstellen, eine davon heißt eventswie folgt:

ID    NAME
1     Sample Event
2     Another Event

Und ein Tisch namens events_metaso:

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

Da repeat_startes sich um ein Unix-Zeitstempeldatum ohne Uhrzeit handelt (1369008000 entspricht dem 20. Mai 2013) und repeat_intervaleinen Betrag in Sekunden zwischen den Intervallen (604800 beträgt 7 Tage).

Durch Durchlaufen jedes Tages im Kalender können Sie mit dieser einfachen Abfrage Wiederholungsereignisse abrufen:

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

Ersetzen Sie einfach jedes Datum in Ihrem Kalender im Unix-Zeitstempel (1299736800).

Beachten Sie die Verwendung des Modulo (% -Zeichen). Dieses Symbol ähnelt einer regulären Division, gibt jedoch den Rest anstelle des Quotienten zurück und ist daher immer dann 0, wenn das aktuelle Datum ein genaues Vielfaches des Wiederholungsintervalls aus dem Wiederholungsstart ist.

Leistungsvergleich

Dies ist erheblich schneller als die zuvor vorgeschlagene "meta_keys" -basierte Antwort, die wie folgt lautete:

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

Wenn Sie EXPLAIN für diese Abfrage ausführen, werden Sie feststellen, dass ein Verknüpfungspuffer erforderlich ist:

+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 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 |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+

Die Lösung mit 1 Verknüpfung oben erfordert keinen solchen Puffer.


"Komplexe" Muster

Sie können Unterstützung für komplexere Typen hinzufügen, um diese Arten von Wiederholungsregeln zu unterstützen:

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

oder

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

Ihre Veranstaltungstabelle kann genauso aussehen:

ID    NAME
1     Sample Event
2     Another Event

Um Unterstützung für diese komplexen Regeln hinzuzufügen, fügen Sie folgende Spalten hinzu 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    

Beachten Sie, dass Sie einfach entweder müssen eine angeben repeat_interval oder einen Satz repeat_year, repeat_month, repeat_day, repeat_week, und repeat_weekdayDaten.

Dies macht die gleichzeitige Auswahl beider Typen sehr einfach. Durchlaufen Sie einfach jeden Tag und geben Sie die richtigen Werte ein (1370563200 für den 7. Juni 2013 und dann das Jahr, den Monat, den Tag, die Wochennummer und den Wochentag wie folgt):

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
  )

Dies gibt alle Ereignisse zurück, die sich am Freitag der 2. Woche wiederholen, sowie alle Ereignisse, die sich jeden Freitag wiederholen, sodass sowohl die Ereignis-ID 1 als auch 2 zurückgegeben werden:

ID    NAME
1     Sample Event
2     Another Event

* Nebenbemerkung in der obigen SQL Ich habe die Standard-Wochentagsindizes von PHP Date verwendet , also "5" für Freitag


Hoffe, das hilft anderen genauso wie die ursprüngliche Antwort mir geholfen hat!

Ahoffner
quelle
6
Das ist unglaublich, danke! Haben Sie eine Idee, wie Sie "alle 2 Monate am ersten Montag" oder "alle 3 Monate am ersten Montag" usw. codieren würden?
Jordan Lev
6
Ich bin damit einverstanden, dass das erstaunlich ist. Ich geriet jedoch in das gleiche Dilemma wie Jordan Lev. Das Feld repeat_interval eignet sich nicht zum Wiederholen von Monaten, da einige Monate länger sind als andere. Wie können Sie die Dauer eines wiederkehrenden Ereignisses begrenzen? Dh alle 2 Monate am ersten Montag für 8 Monate. Die Tabelle sollte ein Enddatum haben.
Abinadi
11
Dies ist eine ausgezeichnete Antwort. Ich habe das repeat_interval gelöscht und das repeat_end-Datum hinzugefügt, aber diese Antwort hat enorm geholfen.
Iain Collins
3
Tipp: Bei komplexen Mustern kann die repeat_intervalSpalte entfernt und in den nachfolgenden Spalten (z. B. repeat_yearusw.) dargestellt werden. In der ersten Zeile kann die Situation der Wiederholung jeden Montag nach dem 20. Mai 2013 durch Platzieren einer 1 in der dargestellt werden repeat_weekdayund ein *in den anderen Spalten.
Musubi
3
@OlivierMATROT @milos Die Idee ist, das Feld, das explizit repariert werden soll, und den Rest auf den Platzhalter zu setzen *. Für "jeden Monat am 3." setzen Sie einfach repeat_day3, die restlichen repeatFelder auf * (lassen Sie repeat_intervalnull) und setzen den repeat_start auf den Unix-Timecode für den 3. März 2011 als Ankerdatum.
Ahoffner
28

Verbesserung: Zeitstempel durch Datum ersetzen

Als kleine Erweiterung der akzeptierten Antwort, die später von ahoffner verfeinert wurde, ist es möglich, ein Datumsformat anstelle eines Zeitstempels zu verwenden. Die Vorteile sind:

  1. lesbare Daten in der Datenbank
  2. Kein Problem mit den Jahren> 2038 und dem Zeitstempel
  3. Entfernungen müssen bei Zeitstempeln, die auf saisonbereinigten Daten basieren, vorsichtig sein, dh in Großbritannien beginnt der 28. Juni eine Stunde vor dem 28. Dezember, sodass das Ableiten eines Zeitstempels von einem Datum den Rekursionsalgorithmus beschädigen kann.

Ändern Sie dazu die DB repeat_start, die als Typ 'Datum' gespeichert werden soll, und repeat_intervalhalten Sie jetzt Tage statt Sekunden. dh 7 für eine Wiederholung von 7 Tagen.

Ändern Sie die SQL-Zeile:

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

zu:

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

alles andere bleibt gleich. Simples!

user3781087
quelle
Was ist, wenn ich möchte, dass sich meine Veranstaltung Jahr für Jahr wiederholt? repeat_interval sollte 365 Tage speichern? Was ist, wenn das Jahr 366 Tage hat?
TGeorge
3
@ George02 Wenn das Ereignis jährlich ist, verlassen Sie repeat_interval NULL und repeat_year ist *. Abhängig von der Wiederholung können Sie repeat_month und repeat_day festlegen, z. B. 11. März oder repeat_month, repeat_week und repeat_weekday, um den 2. Dienstag im April festzulegen.
Jerrygarciuh
25

Ich würde dieser Anleitung folgen: https://github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

Stellen Sie außerdem sicher, dass Sie das iCal-Format verwenden, um das Rad nicht neu zu erfinden, und denken Sie an Regel 0: Speichern Sie einzelne wiederkehrende Ereignisinstanzen NICHT als Zeilen in Ihrer Datenbank!

Gal Bracha
quelle
2
Wie würden Sie Tracking-Benutzer modellieren, die an einer bestimmten Instanz teilgenommen haben? Ist es in diesem Fall sinnvoll, von Regel 0 abzuweichen?
Danny Sullivan
2
@DannySullivan Von Anfang an hätte ich eine andere Entität attendedEventmit baseInstanceIdund instanceStartDate- Das ist zum Beispiel das Basisereignis, aus dem Sie die Kalenderansicht für wiederkehrende Regeln erstellt haben und das Startdatum verwenden, um Informationen zu dieser bestimmten Instanz anzugeben. Dann könnte diese Entität auch habe so etwas wie attendedListIdwas zu einem anderen Tisch führt id,attendedUserId
Gal Bracha
@DannySullivan Ich weiß, es ist eine Weile her, seit du gefragt hast. Außerhalb des vorherigen Kommentars können Sie jedoch jederzeit eine umgekehrte Suche durchführen, um festzustellen, ob dieser Benutzer Teil des Ereigniswiederholungsmusters war. Das würde Ihnen sagen, ob sie zumindest für die Veranstaltung geplant waren. Ob sie tatsächlich anwesend waren oder nicht, ist eine andere Geschichte, die eher DannySullivans Kommentar entspricht.
BRogers
24

Für alle, die daran interessiert sind, können Sie jetzt einfach kopieren und einfügen, um innerhalb von Minuten loszulegen. Ich habe den Rat in den Kommentaren so gut ich konnte befolgt. Lassen Sie mich wissen, wenn mir etwas fehlt.

"KOMPLEXE VERSION":

Veranstaltungen

+ ---------- + ---------------- +
| ID | NAME |
+ ---------- + ---------------- +
| 1 | Beispielereignis 1 |
| 2 | Zweites Ereignis |
| 3 | Dritte Veranstaltung |
+ ---------- + ---------------- +

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 |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +

SQL-Code:

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');

auch als MySQL-Export verfügbar (für einfachen Zugriff)

PHP-Beispielcode 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 />";
    }
?>

PHP-Beispielcode 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
// ----------------------------------------------------------------------------------------------------
?>

Auch der PHP-Code ist hier verfügbar (zur besseren Lesbarkeit):
index.php
und
connect.php Das
Einrichten sollte nun Minuten dauern. Keine Stunden. :) :)

Alex
quelle
2
Wie kann ich abfragen, um alle wiederholten Ereignisse innerhalb eines Datumsbereichs zu erhalten? Das heißt, um alle wiederholten Ereignisse zwischen dem 01.10.2014 und dem 30.12.2014 abzurufen. Vielen Dank für Ihren Beitrag
Well Wisher
@ Wellwisher - wiederholen ... bis und temporäre Tabelle stackoverflow.com/questions/34407833/…
Brad Kent
1
@Alex Wie kann ich ein Instanzvorkommen aus einem wiederholten Ereignis löschen?
Pugazhenthi
1
Ich weiß, dass dies ein alter Thread ist, aber warum der varchar-Typ in repeat_ * -Spalten? Könnten Sie nicht eine Ganzzahl und einen negativen Wert anstelle von '*' verwenden?
Olivier MATROT
1
Danke für den Code. Ich muss jedoch bemerken, dass Ihre Implementierung von Datenbank / Abfragen etwas störend und sehr ineffizient ist. Warum zum Beispiel varchar (255) für so einfache Spalten verwenden (wie @OlivierMATROT erwähnt, könnten Sie ganze Zahlen verwenden, und selbst wenn nicht, warum 255?). Und wenn Sie die Abfrage 30 Mal wiederholen, warum nicht Anweisungen oder Prozeduren verwenden? Ich sage nur um der Sache willen, ob jemand dies umsetzen will.
Rony
15

Während die vorgeschlagenen Lösungen funktionieren, habe ich versucht, sie mit dem vollständigen Kalender zu implementieren, und es würden über 90 Datenbankaufrufe für jede Ansicht erforderlich sein (da sie den aktuellen, vorherigen und nächsten Monat lädt), worüber ich nicht allzu begeistert war.

Ich habe eine Rekursionsbibliothek https://github.com/tplaner/ gefunden, in der Sie einfach die Regeln in der Datenbank speichern und eine Abfrage, um alle relevanten Regeln abzurufen.

Hoffentlich hilft das jemand anderem, da ich so viele Stunden damit verbracht habe, eine gute Lösung zu finden.

Bearbeiten: Diese Bibliothek ist für PHP

Tim Ramsey
quelle
Ich möchte auch den Vollkalender verwenden. Wie könnte mir die Bibliothek helfen? Wie extrahiere ich Propoer-Ereignisse?
Piernik
@piernik - Ich würde die Bibliothek wie in der Dokumentation einrichten und wenn Sie auf bestimmte Probleme stoßen, öffnen Sie eine neue Frage zum Stackoverflow mit dem Code, den Sie eingerichtet haben, und den Problemen, die Sie haben. Ich bin sicher, wenn Sie sich so viel Mühe geben, werden Ihnen einige Mitglieder helfen.
Tim Ramsey
Ich meine, dass WhenSie alle wiederkehrenden Daten in der Datenbank speichern oder alle wiederkehrenden Ereignisse abrufen und Daten in PHP-Nr. In der Datenbank generieren müssen. Habe ich recht?
Piernik
@piernik Sie würden das Anfangsdatum und die Regel (n) in der Datenbank speichern und verwenden When, um alle Daten zu generieren, die aus dem anfänglich gespeicherten Datum / den Regeln gespeichert werden.
Tim Ramsey
Es ist auch nicht gut - Sie können nicht in einem einzigen MySQL-Befehl korrekte Ereignisse erhalten - Sie müssen dafür PHP verwenden.
Trotzdem
14

Warum nicht einen Mechanismus verwenden, der Apache-Cron-Jobs ähnelt? http://en.wikipedia.org/wiki/Cron

Für Kalender \ Zeitplanung würde ich leicht unterschiedliche Werte für "Bits" verwenden, um Standardereignisse für das Wiederauftreten des Kalenders zu berücksichtigen - anstelle von [Wochentag (0 - 7), Monat (1 - 12), Tag des Monats (1 - 31), Stunde (0 - 23), min (0 - 59)]

- Ich würde so etwas wie [Jahr (alle N Jahre wiederholen), Monat (1 - 12), Tag des Monats (1 - 31), Woche des Monats (1-5), Tag der Woche (0 - 7) verwenden. ]]

Hoffe das hilft.

Vladimir
quelle
6
Ich denke, das sind zu viele Wochentagsoptionen. Entweder 1-7 oder 0-6 scheint genauer zu sein.
Abinadi
2
Es ist gut, cron zu verwenden, um die Wiederholung zu speichern. Das Problem ist jedoch, dass es sehr schwierig ist, nachzuschlagen.
Stony
@ Vladimir wie speichern Sie, alle zwei Dienstag (alle zwei Wochen am Dienstag)
Juli
@ Julestruong diese Website sieht aus wie es die Antwort hat: coderwall.com/p/yzzu5a/running-a-cron-job-every-other-week
Ashton Wiersdorf
cron hat eine begrenzte Ausdruckskraft, da es zustandslos ist (lediglich aktuelles / hypotherisches Datum / Uhrzeit mit einem Muster vergleicht) und als solches bestimmte gängige geschäftliche / menschliche Muster wie "jeden dritten Tag" oder "alle 7 Stunden" nicht darstellen kann erfordern das Erinnern an das letzte Vorkommen. Das ist nicht offensichtlich; Sie könnten denken, dass Sie in crontab nur Tag / 3 oder Stunde / 7 sagen, aber am Ende des Monats / Tages haben Sie "übrig gebliebene" Tage / Stunden, die weniger als 3 oder 7 sind. mit möglichen katastrophalen Folgen.
Jaime Guerrero
5

Ich habe nur für diesen Fall eine esoterische Programmiersprache entwickelt. Das Beste daran ist, dass es schemafrei und plattformunabhängig ist. Sie müssen lediglich ein Auswahlprogramm für Ihren Zeitplan schreiben, dessen Syntax durch die hier beschriebenen Regeln eingeschränkt wird.

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

Die Regeln sind erweiterbar und Sie können jede Art von Anpassung hinzufügen, die auf der Art der Wiederholungslogik basiert, die Sie ausführen möchten, ohne sich um Schemamigrationen usw. kümmern zu müssen.

Dies ist ein völlig anderer Ansatz und kann einige eigene Nachteile haben.

Tusharmath
quelle
4

Klingt sehr nach MySQL-Ereignissen, die in Systemtabellen gespeichert sind. Sie können sich die Struktur ansehen und herausfinden, welche Spalten nicht benötigt werden:

   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
quelle
4

Der RRULE-Standard wurde genau für diese Anforderung entwickelt, dh das Speichern und Verstehen von Wiederholungen. Microsoft und Google verwenden es beide in ihren Kalenderereignissen. Bitte lesen Sie dieses Dokument für weitere Details. https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html

Ravinder_Chahal
quelle
3

@ Rogue Coder

Das ist toll!

Sie können einfach die Modulo-Operation (MOD oder% in MySQL) verwenden, um Ihren Code am Ende einfach zu gestalten:

Anstatt:

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

Machen:

$current_timestamp = 1299132000 ;

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

Um dies weiter zu führen, könnte man Ereignisse einschließen, die nicht für immer wiederkehren.

Etwas wie "repeat_interval_1_end", um das Datum des letzten "repeat_interval_1" anzugeben, könnte hinzugefügt werden. Dies macht die Abfrage jedoch komplizierter und ich kann nicht wirklich herausfinden, wie das geht ...

Vielleicht könnte jemand helfen!

dorogz
quelle
1

Die beiden Beispiele, die Sie gegeben haben, sind sehr einfach. Sie können als einfaches Intervall dargestellt werden (das erste ist vier Tage, das zweite 14 Tage). Wie Sie dies modellieren, hängt ganz von der Komplexität Ihrer Wiederholungen ab. Wenn das, was Sie oben haben, wirklich so einfach ist, speichern Sie ein Startdatum und die Anzahl der Tage im Wiederholungsintervall.

Wenn Sie jedoch Dinge wie unterstützen müssen

Ereignis A wird jeden Monat am 3. des Monats ab dem 3. März 2011 wiederholt

Oder

Ereignis A wiederholt den zweiten Freitag des Monats ab dem 11. März 2011

Dann ist das ein viel komplexeres Muster.

Adam Robinson
quelle
1
Ich habe die komplexeren Regeln hinzugefügt, die Sie gerade zu einem späteren Zeitpunkt angegeben haben, aber vorerst nicht. Wie würde ich die SQL-Abfrage modellieren, um Ereignisse am 7. März 2011 abzurufen, damit mein wiederkehrendes Ereignis angezeigt wird?
Brandon Wamboldt