Wiederholende Termine in Datenbank

martinibook

martinibook

Aktives Mitglied
Thread Starter
Dabei seit
20.08.2005
Beiträge
8.730
Reaktionspunkte
350
Hallo,

Ich habe auf einer Webseite Termine, die ich anzeigen möchte. Momentan liegen die einfach in einer MySQL Tabelle und die Start und Endzeit sind jeweils mit einem Timestamp realisiert.

Jetzt sollen die aber noch wiederholbar sein, und zwar täglich, wöchentlich (jeden Donnerstag) und monatlich (jeden 3. Freitag).

Jetzt müsste man irgendwie das so machen, dass alle diese Termine auf der Seite auch angezeigt werden. Meine Idee war, dass immer wenn man einen Termin speichert die Datenbank automatisch für einen gewissen Zeitraum jede Menge Termine in die Datenbank schreibt. MySQL sollte doch eigentlich mit ein paar Tausend Einträgen klarkommen, von daher wäre da eben nur das erstellen dieser Termine.

Es gäbe dann eine Tabelle, in der die Termine mit Wiederholvorschrift drinstehen, eine zweite Tabelle in der dann einfach die Termine wie bisher drin stehen.


Die einzige alternative, die ich jetzt sehe, ist in jeder der Terminübersichten (es gibt 3 komplett verschiedene) die ganzen Wiederholungen dynamisch zu errechnen, so "gibt es in dieser Ansicht eventuell eine Wiederholung eines Termines?".


Übersehe ich da was, oder sind das die Möglichkeiten?
 
Was spricht gegen die Speicher-Variante?
Vom Aspekt der Performance gesehen wäre es optimal, alles andere artet dann in Warterei und Tüfteln aus.
 
Ich würd die nicht für "einen gewissen" Zeitraum in die DB schreiben.
Du könntest dein Datenmodell mit einer weiteren Spalte erweitern die dann z.b. folgendes beinhaltet

0=einmalig
1=wöchentlich
2=monatlich
3=jährlich
4=Mo-FR
5=Mo-SA
6...

Am besten sind 0..n die ID aus einer weiteren Tabelle.

So weisst du immer für jeden Tag im Jahr ob der Termin gilt oder nicht.

Du musst also aus der DB alle in dem Zeitraum mit 0 holen + noch die ab 0 immer. Oder du erweiterst das Datenmodell, das du quasi "template" Termine hast, die kein festen Jahr zugeordnet sind.

Viele Wege führen nach Rom, aber einfach die Inserts in die Datenbank reinschreiben wäre für mich ein no-go.
 
Was spricht gegen die Speicher-Variante?

Es wirkt nicht so elegant, weil man die Daten dann doppelt hat.

Wobei, man könnte ja eine Tabelle mit nur den Start-Daten machen, und in einer anderen dann alles andere. So hätte man letztlich eine Tabelle mit den Terminen, und dann noch eine 1:n Tabelle mit jeder Menge Start-Timestamps.

Alternativ auch den Beschreibungstext 100 Mal speichern.

Nee, dann lieber noch einen LEFT JOIN spendieren.


Viele Wege führen nach Rom, aber einfach die Inserts in die Datenbank reinschreiben wäre für mich ein no-go.

Würde aber die Suche dann später deutlich vereinfachen. Vielleicht ist es sogar schneller, wenn die ganzen Termine schon ausgerechnet sind und nicht immer neu ausgerechnet werden muss, ob es den Termin in dem Zeitraum auch schon gibt.


Du könntest dein Datenmodell mit einer weiteren Spalte erweitern die dann z.b. folgendes beinhaltet

Also letztlich für alle Fälle wie "3. Dienstag im Monat", "3. Mittwoch im Monat" jeweils eine Option machen? In die Mastertabelle bei der redundaten Lösung müsste ich das ja so oder so abbilden …

Jedoch müsste ich dann in jeder Abfrage der Termine immer herumrechnen, welche Termine es gibt und so.



Ihr beiden seit genauso gespalten wie ich. Vor allem regt mich auf, dass die Anforderung natürlich erst kam, als die Terminstruktur schon saß. Immer diese Leute, die sich denken "das ist ja nur eine kleine Funktion" :motz:

Aber wird dann wohl auf die redundante Lösung herauslaufen, die ich dann über 1:n möglichst wenig redundant machen werde.
 
Kommt drauf an, wieviel Optionen es gibt. Wenn es natürlich 20 gibt, kostet das ausrechnen mehr. Es kommt drauf an, wieviel zu gleichzeitig darstellen lassen willst. Bei einer Woche oder einem Monat, was die Regel ist, kostet es auch nicht soviel Zeit 30(Tage)*20(Optionen) zu berechnen ( 600 ). Jede Option bekommt quasi ein Eingangdatum und gibt ja/nein zurück ob es greift. Müsste jeder moderne Webserver hinbekommen. Hast ja auch max 500 User die das nutzen, oder? Und die fragen ja nicht immer gleichzeit an, maximal 5 User gleichzeitig.

Wer sich ein ganzes Jahr anzeigen lassen möchte, der muss halt länger warten. Ist für den User wahrscheinlich auch ok, er wird sich 1 Jahr auch selten anzeigen und da kann man es auch etwas verargumentieren.

Das Problem mit dem "schonmal in die db schreiben"... Finde es desingtechnisch nicht schön und wenn man einen "unendlichen" Kalender haben will, bist schon fast aufgeschmiessen. Moderne Frameworks bieten ja die Möglichkeit schnell zum Jahr 2348 zu gelangen.

Wenn es dann immer noch nicht perfomant ist, gibt es 2 schnelle Lösungen.

Wenn du ein state im Server hast, kannst die Sachen die du 1x berechnet hast auch cachen und bei Veränderungen der Optionen erst invalidieren. Falls state-less, dann würde sich mem-cache anbieten.

Ist für mich definitiv die saubere Variante.

EDIT:

Es ist früh am morgen :) Hab oben einen kleinen Denkfehler im Bsp.
Es geht ja primär um einen User nehm ich an, und was dieser zu sehen bekommt. Er hat sicherlich seine private ( eigen erstellte für ihn termine ) und public ( in der gruppe in welcher er sich befindet ).
Wenn sich also ein User anmeldet und sich eine Monatsübersicht anzeigen lassen will, wird er höchstwahrscheinlich 200 Termine nur haben. Also rund 10! pro Arbeitstag rum. Ist schon eine ganze Menge, da frägt man sich, wann der User auch mal was arbeitet *grins*
Wenn man sich jetzt n-User anzeigen lassen will, dauert es etwas länger. Wie gesagt, ich denke, das muss ein moderner Webserver verkraften und wenn man intelligent cache't geht dies sehr gut. Aber das sind nur Annahmen, ich weiss ja nicht wieviel und was man sich genau anzeigen lassen will. Bin eher so vom "Standard" ausgegangen.

Wär nett wenn du uns mitteilst wie du dich entschieden hast.
 
Zuletzt bearbeitet:
Es sind Termine auf einer Veranstaltungsseite, ich gehe davon aus, das es ungefähr 50 bis 100 Termine im Monat sind (Wiederholungen eingerechnet).

Unendlicher Kalender ist so eine Sache, mehr als 2 Jahre im Vorraus halte ich nicht besonders sinnvoll, so weit wird normalerweise nicht geplant, und die wiederholenden Termine weiß man irgendwann. Das Jahr 2300 ist zwar nett, aber das ist mir recht egal, und die Besucher werden dies Jahr höchstwahrscheinlich auch nicht erleben.

Die redundante Variante:

Beim generieren der Termine, das müsste ja eigentlich nur gemacht werden, wenn Termine verändert werden, dann würde man neue Wiederholungen berechnen.
  • Frage alle Termine und die Wiederholungsmuster ab.
  • Generiere für einen gewissen Zeitraum Timestamps und lege sie in einer anderen Tabelle ab, mit Referenz auf die eigentlichen Termine

Bei der Abfrage würde man dann einfach die Termine abfragen wie bisher.


Bei der nicht-redundanten Variante müsste man beim erstellen der Termine nichts sonderlich machen.

Bei der Abfrage würde es sich dann jedoch schwerer gestalten:
  • Erkennen, was der aktuelle Tag eigentlich ist. Also ein Dienstag, dann noch der dritte Dienstag in diesem Monat.
  • Alle Termine mit entsprechenden Wiederholungen durchgehen und rechnen, ob diese sich heute wiederholen



Also ich glaube, dass es unterm Strich schneller ist, die Termine in einer MySQL Tabelle redundant zu cachen. Außerdem halte ich die Umsetzung von mir schneller zu erledigen.
 
Also ich glaube, dass es unterm Strich schneller ist, die Termine in einer MySQL Tabelle redundant zu cachen. Außerdem halte ich die Umsetzung von mir schneller zu erledigen.

Klar, du bist da der Boss :)

Achso, es handelt sich um eine Veranstallungsseite. Z.b. was bietet der Musikverein 2010/2011. Und Anfang 2011 sagt man dann was 2012 läuft. Wenn der Kunde eh nicht so viel bezahlt, dann hau sie in die Datenbank rein :)

Ich dachte eher sowas wie ein google kalendar oder sunbird, ect. wo man z.b. hab

jeden montag besprechung im grossen raum um 10:00 Uhr
o.ä.

Erkennen, was der aktuelle Tag eigentlich ist. Also ein Dienstag, dann noch der dritte Dienstag in diesem Monat.

Ich dachte, eher sowas wie

Zeige aktuelle Woche an. Dann muss man ja für jeden tag termine anzeigen ob die da gelten. z.b. wie das bsp oben mit 10 uhr. Jetzt muss man ja nur die Tage durchgehen ( alle für diese Woche ) und an die wiederkehrende Termine übergeben. Quasi für jede option gibt es eine vorschrift ( funktion ) die ja/nein zurückgibt. und die funktion hat ja kein problem aus dem datetime objekt zu bestimmen ob es z.b. ein montag ist oder nicht.

Aber wie gesagt, du hast ja einen ganz anderen anwendungsfall und möchtest wahrscheinlich einfach die termine für das ganze jahr/Monat "plain" auf der Setie einfach anzeigen ala

April:
Montag dd.mm.yyyy 18:00 Uhr, grosses Saufen

Also setz es so um wie es für dich momentan am einfachsten ist. Denn der Kunde bekommt das, für das er bezahlt :)
 
:) nett geschrieben.

Ja, letztlich soll da eine Liste sein, die die Termine wie von dir beschrieben anzeigt, dann noch eine Tabelle und eine iCal-artige Ansicht, aber im Prinzip war es das. Alle sehen die gleichen Termine.

Jetzt sind mir eben noch ein paar weitere Fragen gekommen:
Wie sieht es mit Feiertagen aus, gibt es da alle Termine, keine Termine oder nur bestimmte?
Möchte man in einer solchen Serie einzelne Ereignisse löschen können (wenn es mal ausfällt)?
Wenn man als Wiederholung "täglich" aussucht, werden die Termine dann auch über das Wochenende wiederholt?
 
Jetzt sind mir eben noch ein paar weitere Fragen gekommen:
Wie sieht es mit Feiertagen aus, gibt es da alle Termine, keine Termine oder nur bestimmte?

Das würde ich am Termin ausmachen, ala checkbox "gilt immer", oder eben nicht. Wo ich mir mehr sorgen machen würde, Bundesland unterschiedliche Termine (chuckle)

Möchte man in einer solchen Serie einzelne Ereignisse löschen können (wenn es mal ausfällt)?

klar warum nicht. da ist die Methode mit der DB halt nicht so gut. Bei der dynamischen bist da flexibler. Aber bei den relativ festen sehe ich da wenig bedenken. Überleg mal, du machst das für ein Unternehmen mit 500 - 1000 Mitarbeiter die public und ihre persönlichen Termine haben. Jetzt im Voraus 2 Jahre die Dinger anlegen und die User können jederzeit termine löschen,verschieben ect. Viel spass mit der Datenbank performance :) Aber wie gesagt, bei deinem "Musikverein" sehe ich da weniger bedenken.


Wenn man als Wiederholung "täglich" aussucht, werden die Termine dann auch über das Wochenende wiederholt?

klar. heisst auch täglich, ansonsten gilt wie bei meinem bsp

- täglich
- Mo-Fr
- Mo-Sa

So ist es auf jedenfall mit meinem Handy, finde ich ganz nett die Funktion. Denn ich lass mich zur Arbeit gehen morgens wecken, und da ist Mo-Fr Option halt "genial".
 
Hmm, wenn ich denen dann täglich baue, kommt als nächstes, dass die Mo-Fr haben wollen. Jetzt denen erstmal eine Email geschickt und gefragt, was sie exakt haben wollen. Ist halt so eine "Wir hätten gerne eine Webseite"-Fraktion …
 
Hm, ich haette jetzt eine zusaetzliche Spalte mit Intervallen angelegt, wo der Abstand, mit dem der Termin wiederholt wird, eingetragen wird. Wenn z. B. ein Termin woechentlich stattfindet, kommt in diese Spalte der Wert (60*60*24*7 Sekunden =) 604.800 Sekunden rein. Bei Nichtwiederholung und standardmaessig "NULL".

Besonders elegant waere eine VARCHAR-Spalte, die direkt mit dem String gefuettert wuerde, die strtotime() zu Errechnung der Wiederholung benoetigt. Z. B. "+1 week", "+2 months", "+905865", "next friday" etc. Nur mit Sekunden-Werten liesse sich kein Termin an jedem dritten Freitag des Monats bewerkstelligen, der Wochentag wuerde springen.

Wenn der Kalender abgerufen wird, werden alle Datensaetze aus der DB geholt, die in dieser Intervall-Spalte nicht NULL haben oder bei denen der Starttermin noch in der Zukunft liegt. Anschliessend werden die Start- und die Intervall-Werte bis zu einem bestimmten Datum (heute, heute naechster Monat, heute naechstes Jahr etc.) addiert. Am besten einen Array mit den resultierenden Timestamps fuettern. Ausgabe ist dann ja "simpel".

Fuer Feiertage etc. zwei weitere Tabelle anlegen – eine fuer die Feiertage selbst, die andere fuer Ausweichtermine, beide "overriden" dann die eigentliche Termintabelle.

Kann mir kaum ein Unternehmen vorstellen, das selbst nach Jahren derart viele Termine in der Datenbank haette, dass es den Server auch nur zum Gaehnen bringen wuerde, die Termine bei jedem Zugriff neu auszurechnen. Zumal ja nur wiederholende und zukuenftige Termine errechnet werden muessen.

Ausserdem beschraenkt sich die Berechnung der Termindarstellungen ja nur auf das aktuell dargestellte Zeitfenster (Wochen-, Monats-, Jahresansicht).
Aber selbst das Darstellen eines ganzen Jahrzehnts mit 20 oder 40 woechentlichen Terminen stellt einen Server vor keine echte Herausforderung, die Seite hast du in < 1 Sekunde ausgegeben.

P.S.: Ich bin immer wieder begeistert, welche Strings strtotime() akzeptiert – probiert mal:
PHP:
<?php
	print date("d.m.Y", strtotime('+3 weeks next friday'));
?>
 
Okay, also mache ich einfach ein:
PHP:
$startzeit = $datenbank_loop['startzeit'] // Hole Daten aus der Datenbank
if ($datenbank_loop['wdh'] != null) { // Wenn eine Wiederholung eingrragen ist ...
  while ($startzeit < time()) // Wenn die Startzeit in der Vergangenheit liegt ...
    $startzeit += strtotime($datenbank_loop['wdh']) // solange "wiederholen", bis in der Gegenwart liegt.
}
ausgabe($startzeit, $datenbank_loop['...']);

Hmm. Joa, das wäre ja eigentlich nicht sooo schwer. Wäre dann auch bei der Erstellung und Wartung gezielter.

Jedoch kam jetzt die Anforderung, einzelne Aufkommen zu löschen. Von daher ist die Vorausberechnung und das manuelle löschen dann schon einfacherer.

Aber ich kann strtotime() dann immerhin benutzen um die Wiederholungen zu berechnen. Ich hatte da bisher einen Wust von date() und mktime() gesehen …
 
Wiederholung würde ich immer als einzelne Events speichern. Das ist im Gegenteil sogar eleganter, als eine weitere Spalte einführen. Dinge, die schwer sind wenn Du Wiederholung in eigener Spalte definierst:

- Ab einem bestimmten Zeitpunkt Wiederholung ändern, die restlichen Daten beibehalten
- Ein Event löschen, alle anderen Wiederholungen vorher und nachher belassen
 
Die Frage wäre noch, welche Daten pro Vorkommen gespeichert werden sollen, und welche Daten zentral gespeichert werden.

Meine Idee:

pro Vorkommen:
  1. Anfangszeit

Zentral:
  1. Dauer
  2. Wiederholungsschema
  3. Ort
  4. Beschreibung
  5. Status
  6. Kategorie


Ist halt die Frage, was man von den Sachen noch pro Ereignis verändern möchte. Obwohl, eigentlich habe ich eine Liste mit Anforderungen, die setze ich jetzt um, und der Rest ist dann einfach Pech.
 
Ich würde so vorgehen:

pro Vorkommen: (nichts zentral)
1. Anfangsdatum und Uhrzeit
2. Enddatum und Uhrzeit
3. Ort
4. Beschreibung
5. Status
6. Kategorie
7. ID
8. Vater-ID (wenn vorhanden)

Wenn sich die Information eines Termins ändert, guckst Du, ob es andere Events in der Zukunft mit ID=Vater-ID gibt. Wenn ja, fragst Du, ob alle zukünftigen verändert werden sollen, oder nur der jetzige. Soll nur der jetzige verändert werden, löschst Du aus diesem die Vater-ID und er ist ein eigenständiger Termin.

Alle Ansätze, solche Art von Daten über Konventionen zu speichern (Dauer statt Enddatum, Beschreibung nur einmal, etc.) hören sich nur oberflächlich "geschickt" an. Sie verkomplizieren Deinen Kalender und Du wirst wahrscheinlich recht bald auf eine Mauer stoßen.

Lange Rede kurzer Sinn: die erste Antwort von wegus.
 
Und dann mache ich mir noch eine weitere Tabelle die einfach Vater-IDs enthält (mit Auto Increment) und sonst nichts?

Dadurch wären natürlich alle Termine im Prinzip Termine wie zuvor auch. Wenn man das ganze jedoch für 5 Jahre in die Zukunft baut, entstehen da mal eben 10 MB Datenbank. Obwohl die ja dann damit fertig werden sollte.

Nur was mache ich, wenn die Datenbank voll wird?
 
Du brauchst nur eine Tabelle. Die Vater-IDs sind die IDs der Vater-(d.h. Ursprungs-)Termine. Die Kategorien müssten wahrscheinlich in eine eigene Tabelle.

MySQL hat ersteinmal keine Beschränkung der Größe. Es ist eher wahrscheinlich, dass Dein Provider eine festes Größenlimit festlegt. Wie kommst Du auf 10 MB in 5 Jahren? Das wären 2500 Termine pro Jahr.

Wenn Du trotzdem Angst hast, oder sehr viele Termine kannst Du auch periodisch alle löschen, die älter als ein bestimmter Punkt in der Vergangenheit sind.

Ungefähre Hochrechnung, Zahlen in bytes:
1. Anfangsdatum und Uhrzeit 25
2. Enddatum und Uhrzeit 25
3. Ort 250
4. Beschreibung 250
5. Status 250
6. Kategorie 5
7. ID 5
8. Vater-ID (wenn vorhanden) 5

26 + 26 + 250 + 250 + 250 + 5 + 5 + 5 = 817 bytes

10 * 1.024 * 1.024 = 10.485.760 / 817 = 12.834 Termine in 10 MB

12.834 / 5 Jahre = 2.566,8 Termine pro Jahr
 
Zurück
Oben Unten