phpbar.de logo

Mailinglisten-Archive

[php] Re: pm Re: [php] Doofer MySQL Timestamp SELECT (Veranstaltunskalender, Termine verwalten)

[php] Re: pm Re: [php] Doofer MySQL Timestamp SELECT (Veranstaltunskalender, Termine verwalten)

Martin Franz martin_(at)_franz63.de
Fri, 12 Jan 2001 16:06:35 +0100


Hallo Hans,


>nun, ich habe auch noch keine aufgebaut. Da ich hier im Allgäu
>ein Infoportal aufsetzen will, muss ich mir überlegen wie die

im Allgäu - ich dachte im Internet ;-)

>- Branchendatenbank mit Zusatzinfos (256 Zeichen Suchbegriffe,
> mehrere Brancheneinträge je Kunde...)
>
>  und eben der
>
>- Veranstaltungskalender
>
>zu machen sind. Und Datenbank Design ist ja die Vorraussetzung für
>eine gute schnelle Anwendung.

Wohl wahr!

>
>Verschiedene andere Sachen habe ich schon entworfen aber grad beim
>Veranstaltungskalender fällt mir keine so gute Lösung ein.

Hmm, hab auchschon so einiges gemacht - also sollt einem doch hier auch was
einfallen :-)

>Der Haken ist ja, wie organisiert man Termine die wiederkehrend sind?
>Einmal Termine ist ja leicht. Aber wie macht man das mit "Turnen jeden
Dienstag"
>oder noch dümmer, jeden ersten Donnerstag im Monat. :-|
>Da habe ich noch nicht mal ne gute Idee. Denn das was Du mit 2 Tabellen
beschrieben
>hast, scheint mir etwas umständlich zu sein, zudem habe ich es nicht
kapiert.

Ok vielleicht versuch ich das nochmal zu erklären - ist zugegeben nicht so
einfach und auch nicht die performanteste Lösung, aber ich glaub für gewisse
Probleme mit Mysql ist es einfach auch die einzigste Lösung.
Also folgendes Problem:

In der Veranstaltungsdatenbank kann mit jedem Datensatz maximal ein Tag
beschrieben werden, das heisst wenn jetzt eine Veranstaltung 5 Tage dauert,
hat sie auch 5 Datensätze. Angenommen diese Veranstaltung findet aber jetzt
mehrmals im Jahr statt - sozusagen in mehreren Blöcken.
Selecten will ich nun immer nur den Anfang und das Ende jedes Blockes - so
dass ich als output dann:

row1 veranstaltung start
row2 veranstaltung ende //Block 1
row3 veranstaltung start
row4 veranstaltung ende //Block2

erhalte.

Ich muss also irgendwie eine Bedingung in den Query packen dass ich nur die
Rows will, auf die eine weitere Row folgt, von der selben Veranstaltung -
nur eben der nächste Tag. D.h. die erste Row ist der Anfang einer
Veranstaltung. Und ich will die Rows, zu denen es eine "Vorgänger" row gibt,
aber keinen "Nachfolger" - das ist dann das Ende eines Blockes.
Da ist dann auchschon das Problem: Row übergreifende Bedingung.
hmm.. noch ein Bildchen dann ists einfacher:

<datenbank inhalt>
Datum1                Veranstaltung1   <--- Start des Blockes // Soll
selected werden
Datum1+1Tag      Veranstaltung1
Datum1+2Tage    Veranstaltung1    <--- Ende des Blockes // Soll selected
werden
<-- ... einige Zeit dazwischen --->
Datum2                Veranstaltung1   <--- Start des Blockes // Soll
selected werden
Datum2+1Tag      Veranstaltung1
Datum2+2Tage    Veranstaltung1    <--- Ende des Blockes // Soll selected
werden
</datenbank inhalt>
(ich setzte ein ORDER BY datum voraus)
Gut - jetzt kommt der Trick.
wenn ich nur eine Tabelle hab hab ich ja in der Where-clause keine
informationen darüber was z.b. in dem folgendem Datensatz drinsteht - das
müsste ich aber wissen, um sagen zu können ob dieser Datensatz jetzt der
letzte Tag einer Veranstaltung ist oder ob der folgende Datensatz wieder von
dieser Veranstaltung ist und die Veranstaltung also noch weiter geht.
ok?
gut - trick also. Ich hab nur eine Tabelle - also mach ich in meinem Select
2 draus - Mysql kann das - ist zwar nicht so schnell weil (weis jetzt nicht
genau wie mysql das handhabt) ich ja die Tabelle praktisch temporär einmal
kopiere.
Gut das macht man also so:
"SELECT a.* FROM veranstaltungen a  LEFT JOIN  veranstaltungen b"

Ich habe die Tabelle "veranstaltungen " und geb ihr 2 alias, nämlich a und
b -> 2 Tabellen a,b mit selben Inhalt

jetzt noch die Join Bedingung, also z.b. die Veranstaltungsnummer, die immer
gleich ist.

"SELECT a.* FROM veranstaltungen a  LEFT JOIN  veranstaltungen b ON
vanummer"

d.h. alle Datensätze in a werden mit denen aus b verschmolzen die die selbe
vanummer haben - naja, das bringt nochnicht soviel - schliesslich will ich
ja wissen ob ein gewisser Datensatz einen "Vorgänger" oder "Nachfolger" hat.
also:
"SELECT a.* FROM veranstaltungen a  LEFT JOIN  veranstaltungen b ON vanummer
WHERE a.datum = SUBDATE(b.datum, INTERVAL 1 DAY)
OR a.datum = ADDDATE(b.datum, INTERVAL 1 DAY)"

Das gibt dann alle Veranstaltungsblöcke aus - so far noch nichts besonderes,
und tierisch umständlich.. weiter aber:

"SELECT a.* FROM veranstaltungen a  LEFT JOIN  veranstaltungen b ON vanummer
WHERE
(a.datum = SUBDATE(b.datum, INTERVAL 1 DAY)
    AND
    a.datum != ADDDATE(b.datum, INTERVAL 1 DAY))
OR
(a.datum = ADDDATE(b.datum, INTERVAL 1 DAY)
    AND
    a.datum != SUBDATE(b.datum, INTERVAL 1 DAY)"

Das müssts sein, ich habs zwar nich ausprobiert, aber ich denke es müsste
gehen. Wie gesagt sollte man noch ein "ORDER BY datum" verwenden und falls
man nur nach einer Veranstaltung sucht - noch ein "AND Where a.vanummer =
1234567"
Ich bekomme also alle Rows die einen Nachfolger, aber keinen Vorgänger haben
(Start) und alle Rows die einen Vorgänger, aber keinen Nachfolger haben.
(Ende)

Kann sein dass sich die Where-clause durch etwas Logik vereinfachen lässt,
aber das muss jetzt nicht sein.
Uff, soweit zum Tobi Problem.

(Tobi, probiers bitte mal aus, würd mich interessieren obs geht - bzw. bin
mir recht sicher das es gehen muss - aber ob der query so schon stimmt..)


>Es kommt ja auch noch drauf an wie man das abfragen will. Was man
mindestens
>machen muss ist ja die tägliche Abfragemöglichkeit oder was ist innerhalb
>eines definierten Zeitraumes an Terminen da. Das ist alles noch recht
einfach,
>aber wie sollte man das mit dem Stammtisch der jeden ersten Donnerstag
stattfindet
>in den Griff bekommen. Denn der Kunde soll ja eingeben können, "Stammtisch"
ist
>von April bis Oktober an jedem ersten Donnerstag im Monat.
>
>Und für die Aufgabe habe ich noch keine praktikalbel Idee.

Hmm.. mir ist da grade ein Vorbild eingefallen - Falls Du Dich unter UNIX
auskennst, da gibts ja jemanden der sich CRON nennt :-) (also der Daemon der
periodische Prozesse ausführt - z.b. jede Nacht um 12 ein backup fahren oder
sowas..)
CRON hat eigentlich ein recht leistungsfähiges System um seine Termine zu
koordinieren.
Er verwendet folgendes format:

5 Werte, durch Lerzeichen getrennt
- Minute (0-59)
- Stunde (0-23)
- Tag des Monats (1-31)
- Monat im Jahr (1-12)
- Tag der Woche (0-6) // 0=Sonntag, 6=Samstag

Zitat UNIX, Markt&Technik, 93':
"Mehrere Zeitangaben können durch Kommata getrrennt angegeben werden.
Zeitbereiche können durch einen Bindestrich "-" angegeben werden. Das
Zeichen  "*" bedeutet dass alle erlaubten Werte verwendet werden."
Damit würde "Montags, 16h turnen, von Februar bis August" so aussehen:

0 16 * 2-8 1

Damit lässt sich wirklich jeder Termin ausdrücken, ob über mehrere Tage,
wöchentlich, monatlich oder was auch immer.
(*grübel* nur jährlich nicht, aber das liesse sich ja implementieren)

Hmm - soweit ein funkitonierendes System - jetzt muss man das nurnoch so
umbauen, dass es elegant in einer Datenbank abgelegt werden kann - und da
muss ich erstmal ein bisschen grübeln..
Zum grübeln bin ich jetzt kurz aufs Klo gegangen, da fällt einem sowas immer
ein - also gerade tönte durch ganz Passau der aufschrei "ich habs! verdammt
spinn ich oder was!"
folgendes: (ist mir wirklich grad am klo eingefallen, kein witz)
eine Woche hat 7 Tage, ein Monat 31, ein Tag 24 Stunden, eine Stunde 60
Minuten.
toll, gell ;-)
gut, trick:
7 Tage - ein Byte hat 8 Bit
31 Tage - 4 Byte haben 32 Bit
24 Stunden -  3 Byte haben 24 Bit
60 Minuten - 8 Byte haben 64 Bit
den Ausdruck "-" von cron vergessen wir, den brauchen wir nicht.
Ich mache es so:
5 Felder in der Datenbank um die Zeit zu codieren:

- minute BIGINT (8Bytes, 64Bit)
- hour MEDIUMINT (3Bytes, 24Bit)
- dayofmonth INT (4Bytes, 32Bit)
- month SMALLINT (2Bytes, 16Bit)
- dayofweek TINYINT (1Byte, 8Bit)

oops, alle UNSIGNED nicht vergessen.

Wir verwenden einfach die Bitcodierung um die Werte auszudrücken.
z.b. Januar ->1tes bit im month Feld ist 1
16:50 Uhr -> 16tes bit in hour ist 1, 50tes Bit in minute ist 1.

nochmal das Beispiel mit dem Turnen, Montags 16h von Januar bis August
- minute = 0
- hour = 16tes Bit gesetzt, sonnst alles 0 also das wäre der Wert 256
dezimal
- dayofmonth = 0
- month = 1-8tes Bit ist 1,  der Rest 0 -> Wert 65280 dezimal
- dayofweek = 1tes Bit ist 1, der Rest 0 -> Wert 128


Was hältst Du davon?
wenn man direkt auf der Datenbank arbeitet ist das zwar etwas
unverständlich. aber mysql hat ja die demensprechenden Bit Funktionen um die
Werte zu bearbeiten und auszudrücken.

Ich sehe hier ne Menge Vorteile:
- jeder Termin braucht nur eine Zeile
- Speicheraufwand ist recht gering, Feldergrössen sind konstant
- Speicheraufwand für beliebigen Termin sind 21Byte - das ist weniger als 2
Timestamp14 Felder
- Komfortabel um Termine auszuwerten - z.b.
"select * from termine where dayofweek = 128" // alle Termine die an
Montagen stattfinden
"select * from termine where month & 16 > 0" // alle Termine die im Mai
stattfinden
"select * from termine where month & 255 > 0" // alle Termine die zwischen
Januar und August stattfinden

Vielleicht sollte man bei den Minuten von einer Bitcodierung absehen,
schliesslich hat man ja selten Termine die z.b. alle 5 minuten Stattfinden -
naja, wie mans braucht - man spart damit 2 Byte :-)

Um die Werte zu erzeugen Setzt man sich am besten Konstanten und verwendet
Bitmasken, also z.b.
$JANUAR = 1;
$FEBRUAR = 2;
$MÄRZ = 4;
$APRIL = 8;
$MAI = 16;
$JUNI = 32;
$JULI = 64;
....
und dann einfach
$wert = $JANUAR & $MÄRZ & $APRIL;
"select * from termine where month & '$wert' > 0" // termine die im Januar
und März und April stattfinden

$wert = $JANUAR | $FEBRUAR
"select * from termine where month & '$wert' > 0" // termine die im Januar
oder Februar stattfinden

Nachteil ist halt echt nur das Bit-gefiesel, aber mit Konstanten wirds easy
und sehr angenehm zu lesen.
Aber mal ehrlich - ich find das genial!
hmm - das wär ja glatt ein Thema für nen Artikel auf PHP-Center.de , meinst
Du nicht auch?
Thema ist hiermit für einen Artikel reserviert :-)
Verdammt hab ich jetzt nen Roman geschrieben - naja - Spass hats gemacht!
Doch halt - fällt mir grade noch ein Manko ein:
Wie codiere ich die Dauer der Veranstaltung minutengenau?
z.b. Turnen täglich von 16:15 bis 18:35
*grübel* naja, vielleicht noch 2 felder dazu .hmm.. leider muss ich grad
nicht aus klo ;-)
also sowas wie Turnen von 16-18 Uhr geht schon.. aber die anfangs und
end-Minuten kann ich nicht ausdrücken.
hmm.. vielleicht irgendeine bit-komplementdarstellung - dann wirds übel,
dann hab ich noch ein Vorzeichenbit - naja, am Klo dann..
bzw. in meinem Artikel ;-))
>Du siehst, alles nicht so einfach.

na wie man sieht - auch hier nicht so einfach :-)

>Was machst Du denn in Passau, wohnst Du da oder studierst Du dort?
(Wahlbayer? ;)
Ja, am Politischen Aschermittwoch lass ich hier meine Parolen hören ;-)
schmäh ohne - ich Studier hier Informatik, 1tes Semester.

>Liebe Grüße aus dem Allgäu

Greetings auch wieder aus Passau
>Hans Theo Mislisch
martin



php::bar PHP Wiki   -   Listenarchive