MySQL: Alle abgedeckten Monate/Tage erkennen
AllesMeins
- datenbank
0 dedlfix0 Ilja0 AllesMeins0 AllesMeins0 dedlfix
Hiho,
ich habe in einer Datenbank eine Anzahl von Terminen gespeichert. Zu jedem Termin gibt es ein DATETIME Feld mit dem Startpunkt und ein DATETIME Feld mit dem Endzeitpunkt.
Generell gibt es keine Einschränkungen für die Dauer eines Termines, sie können also innerhalb eines Tages stattfinden oder sich über mehrere Tage oder gar Monate ziehen.
Nun möchte ich gerne möglichst effektiv zwei Dinge herausfinden:
Auf Seite 1 bräuchte ich eine Monatsübersicht, also es sollen alle Monate angezeigt werden in denen mind. ein Termin liegt. Dabei kann es auch vorkommen das zum Beispiel ein Termin im Januar startet und im März endet, dann soll aber trotzdem der Februar auch entsprechend hervorgehoben angezeigt werden.
Auf Seite 2 möchte ich das selbe Spiel mit einer Tagesauswahl machen. Es sollen alle Tage eines bestimmten Monats angezeigt werden, die von einem termin überdeckt oder berührt werden.
Kann mir irgendwer einen Schubs in die richtige Richtung geben, wie ich meine mySQL Abfrage am besten gestalte um möglichst Effektiv an die benötigten Infos zu kommen?
Marc
echo $begrüßung;
Zu jedem Termin gibt es ein DATETIME Feld mit dem Startpunkt und ein DATETIME Feld mit dem Endzeitpunkt.
Auf Seite 1 bräuchte ich eine Monatsübersicht, also es sollen alle Monate angezeigt werden in denen mind. ein Termin liegt.
Auf Seite 2 möchte ich das selbe Spiel mit einer Tagesauswahl machen.
Kann mir irgendwer einen Schubs in die richtige Richtung geben, wie ich meine mySQL Abfrage am besten gestalte um möglichst Effektiv an die benötigten Infos zu kommen?
Bei einem mehrere Tage dauernden Ereignis möchtest du sozusagen für jeden Tag ein Ergebnisdatensatz haben. Bei mehrfach vorkommenden Tagen hilft ein DISTINCT, die Dopplungen zu entfernen, das ist also kein Problem. Doch wie erzeugt man aus einem Datensatz mehrere definierte Ergebniszeilen?
Da ich dazu keine Lösung kenne würde ich die betroffenen Tage in der verwendeten Programmierumgebung ermitteln. Zunächst aber zum SQL-Teil:
WHERE
(start >= monatserster AND start <= ultimo) OR # StartDatum im betroffenen Monat
(ende <= ultimo AND ende >= monatserster) OR # EndeDatum innerhalb des Monats
(start < monatserster AND ende > ultimo) # Monat liegt zwischen Start und Ende
sollte alle Datensätze ermitteln, die den abzufragenden Monat betreffen. Nun sollen in den Ergebnisdatensätzen keine Tage außerhalb des Monats enthalten sein. Wenn das Startdatum vor dem Monatsersten liegt soll stattdessen der Monatserste angezeigt werden. Analog beim Monatsende.
SELECT
DAYOFMONTH( IF( start < monatserster, monatserster, start)) AS starttag,
DAYOFMONTH( IF( ende > ultimo, ultimo, ende)) AS endtag
In der Programmierumgebung muss nun noch für jeden Ergebnisdatensatz in einer von starttag bis endtag laufenden Schleife für jeden Tag "ein Kreuzchen im Kalender" gemacht werden.
Der Ultimo lässt sich soweit ich weiß nicht mit MySQL ermitteln, aber beispielsweise in PHP mit date('t', $ein_datum_aus_dem_Monat)
Dieses Prinzip sollte sich auch für ein Jahr anwenden lassen.
echo "$verabschiedung $name";
yo,
Doch wie erzeugt man aus einem Datensatz mehrere definierte Ergebniszeilen?
man kann dies errreichen, indem man sich mit zwei zusätzlichen tabellen behilft, könnten auch temporär sein, bzw. gleich in die Abfrage mit eingebaut werden. diese beinhalten nichts anderes als die monats zahlen 1-12, bzw. die tie tage 1 bis 31. damit sollte sich dann ein JOIN schaffen lassen, der genau das gewollte leistet.
was man zusätzlich noch leisten muss ist, die start- und dateumsendwerte mit den ensprechenden funtionen in Monats- und Tageszahl umzuwandeln und deinen angespreochenen DISTINCT verwenden.
Ilja
Hiho,
ich muss zugeben das ich noch nie mit komplexeren Dateumoperationen in MySQL gearbeitet habe. Daher scheitert es bei mir schon daran überhaupt erst mal irgend ein Datum von PHP in MySQL zu bringen. Das einzige, das ichd a kenne ist FROM_UNIXTIME(). Aber es wäre ja ziemlich sinnlos mein in Tagen und Monaten vorliegendes Datum erst per PHP in einen timestamp umzuwandeln und dann wieder zurück. Gibt es da irgend einen direkteren Weg?
Das selbe gilt für den letzten tag des Monats. Auch hier fällt mir nichts ein um den effektiv zu berechnen. Da müsste ich ja auch erst mal das Datum in nen timestamp umwandeln, dann mit date() den letzten Tag rausfinden, mit den neuen Daten wieder nen timestamp errechnen und den dann wieder in mySQL umwandeln. Das kann es doch irgendwie nicht sein :)
Grüsse
Marc
echo $begrüßung;
Zu jedem Termin gibt es ein DATETIME Feld mit dem Startpunkt und ein DATETIME Feld mit dem Endzeitpunkt.
Auf Seite 1 bräuchte ich eine Monatsübersicht, also es sollen alle Monate angezeigt werden in denen mind. ein Termin liegt.
Auf Seite 2 möchte ich das selbe Spiel mit einer Tagesauswahl machen.
Kann mir irgendwer einen Schubs in die richtige Richtung geben, wie ich meine mySQL Abfrage am besten gestalte um möglichst Effektiv an die benötigten Infos zu kommen?Bei einem mehrere Tage dauernden Ereignis möchtest du sozusagen für jeden Tag ein Ergebnisdatensatz haben. Bei mehrfach vorkommenden Tagen hilft ein DISTINCT, die Dopplungen zu entfernen, das ist also kein Problem. Doch wie erzeugt man aus einem Datensatz mehrere definierte Ergebniszeilen?
Da ich dazu keine Lösung kenne würde ich die betroffenen Tage in der verwendeten Programmierumgebung ermitteln. Zunächst aber zum SQL-Teil:
WHERE
(start >= monatserster AND start <= ultimo) OR # StartDatum im betroffenen Monat
(ende <= ultimo AND ende >= monatserster) OR # EndeDatum innerhalb des Monats
(start < monatserster AND ende > ultimo) # Monat liegt zwischen Start und Endesollte alle Datensätze ermitteln, die den abzufragenden Monat betreffen. Nun sollen in den Ergebnisdatensätzen keine Tage außerhalb des Monats enthalten sein. Wenn das Startdatum vor dem Monatsersten liegt soll stattdessen der Monatserste angezeigt werden. Analog beim Monatsende.
SELECT
DAYOFMONTH( IF( start < monatserster, monatserster, start)) AS starttag,
DAYOFMONTH( IF( ende > ultimo, ultimo, ende)) AS endtagIn der Programmierumgebung muss nun noch für jeden Ergebnisdatensatz in einer von starttag bis endtag laufenden Schleife für jeden Tag "ein Kreuzchen im Kalender" gemacht werden.
Der Ultimo lässt sich soweit ich weiß nicht mit MySQL ermitteln, aber beispielsweise in PHP mit date('t', $ein_datum_aus_dem_Monat)
Dieses Prinzip sollte sich auch für ein Jahr anwenden lassen.
echo "$verabschiedung $name";
Sorry fürs TOFU - hab nicht aufgepasst...
Grüsse
Marc
echo $begrüßung;
Aber es wäre ja ziemlich sinnlos mein in Tagen und Monaten vorliegendes Datum erst per PHP in einen timestamp umzuwandeln und dann wieder zurück. Gibt es da irgend einen direkteren Weg?
Nun, Jahr und Monat hast du angenommenerweise bereits in $jahr und $monat vorliegen. Daraus lässt sich der Monatserste recht einfach bilden:
$erster = sprintf(%s-%s-%s, $jahr, $monat, 1);
Nur um den Monatsletzten mit date() zu ermitteln braucht es einen PHP-Timestamp. mktime() liefert einen solchen. Zusammengefasst ergibt das:
$ultimo = sprintf(%s-%s-%s, $jahr, $monat, date('t', mktime(0, 0, 0, $monat, 1, $jahr))); // [*]
Damit haben wir zwei MySQL-konforme Datumsstrings, die nun in einer Abfrage untergebracht werden können:
$sql = sprintf("SELECT * FROM %s
WHERE Beginn
>= '%s' AND Ende
<= '%s'", $tabelle, $erster, $ultimo);
War doch gar nicht so schwer, oder? :-)
echo "$verabschiedung $name";
[*] Natürlich lasst sich das auch etwas übersichtlicher mit den einzelnen Zwischenergebnissen in jeweils eigene Zeilen schreiben.