[MYSQL]Timestamps

DJ_Freeman

Well-known member
5 Juni 2006
92
3
Moin leute, ich steh hier irgendwie grade auf dem logik-schlauch, also ich hoffe ihr könnt mir helfen ;)

Also ich hab 2 [unix-]timestamps, start und ende in der tabelle und möchte nu herausfinden, welche einträge sich mit 2 weiteren timestamps(auch start und ende) überschneiden.

Bsp:

Eintrag in der Tab: start = 0, ende = 5
Vergleichsdaten z.b.: start = 1, ende = 5

Ich will jede art von überschneidung drin haben, wie muss ich das query gestalten?

Bisher bin ich soweit

Select id from tab where (start = '".$start."' AND ende = '".$ende."') OR (start = '".$start."' AND ende < '".$ende."') OR (start < '".$start."' AND ende = '".$ende."') OR (start < '".$start."' AND ende > '".$ende."') OR (start > '".$start."' AND ende < '".$ende."') OR (start > '".$start."' AND ende = '".$ende."') OR (start = '".$start."' AND ende > '".$ende."')

Findet ihr noch vereinfachungen bzw. übersehene Fälle?

DJ_Freeman
 
müsste doch eigentlich auch mit folgendem gehen oder ?

PHP:
Select id from tab where (start >= '".$start."' AND ende <= '".$ende."')

oder hab ich da nen Denkfehler drin ??
 
müsste doch eigentlich auch mit folgendem gehen oder ?

PHP:
Select id from tab where (start >= '".$start."' AND ende <= '".$ende."')

oder hab ich da nen Denkfehler drin ??

Wie gesagt, es sollen alle überschneidungen sein, dein code behandelt ja nur vergleichsstart vor bzw. gleich dem der db und vergleichsende nach bzw. gleich dem der db...

Es gibt ja auch noch später anfangen und früher aufhören, etc pp. Nach deinem können die vergleichs stamps ja auch in nem anderen liegen und nicht "entdeckt werden"
 
Öhrm, den Operator >=< wird's wohl nirgends geben. Der würde ja "größergleichkleiner" aussagen und das trifft auf keinen einzigen Fall zu.

Das Problem lässt sich ja auf 3 Fälle runterbrechen:
  1. Der neue Eintag ist innerhalb von Start und Ende.
  2. Der neue Eintrag beginnt vor Start und endet nach Start.
  3. Der neue Eintrag beginnt vor Ende und endet nach Ende.
Also:
Code:
SELECT id
FROM tab
WHERE (start >= {$start} AND ende <= {$ende})
  OR (start BETWEEN {$start} AND {$ende})
  OR (ende BETWEEN {$start} AND {$ende})
Damit sollten eigentlich alle Fälle abgedeckt sein. Hoffentlich vertue ich mich grad nicht, aber ich glaube nicht... :think:
 
Zuletzt bearbeitet:
Öhrm, den Operator >=< wird's wohl nirgends geben. Der würde ja "größergleichkleiner" aussagen und das trifft auf keinen einzigen Fall zu.

Das Problem lässt sich ja auf 3 Fälle runterbrechen:
  1. Der neue Eintag ist innerhalb von Start und Ende.
  2. Der neue Eintrag beginnt vor Start und endet nach Start.
  3. Der neue Eintrag beginnt vor Ende und endet nach Ende.
Also:
Code:
SELECT id
FROM tab
WHERE (start >= {$start} AND ende <= {$ende}
  OR (start BETWEEN {$start} AND {$ende})
  OR (ende BETWEEN {$start} AND {$ende})
Damit sollten eigentlich alle Fälle abgedeckt sein. Hoffentlich vertue ich mich grad nicht, aber ich glaube nicht... :think:

Hmm interessanter Ansatz, gibts noch weitere ideen?

Imo dürfte wirklich alles abgedeckt sein...
 
Mir ist jetzt grad beim Drübergucken noch aufgefallen, dass der erste Fall ja auch durch die beiden anderen Fälle abgedeckt wird. Insofern würd sich die Query noch weiter reduzieren, indem einfach nur geguckt wird, ob der neue Start- bzw Endzeitpunkt innerhalb der vorhandenen Zeitfenster liegt.
 
Mir ist jetzt grad beim Drübergucken noch aufgefallen, dass der erste Fall ja auch durch die beiden anderen Fälle abgedeckt wird. Insofern würd sich die Query noch weiter reduzieren, indem einfach nur geguckt wird, ob der neue Start- bzw Endzeitpunkt innerhalb der vorhandenen Zeitfenster liegt.

Naja gut, dass wäre ein wenig eleganter, die frage ist, ob noch ein fall fehlt...

DJ_Freeman
 
Naja gut, dass wäre ein wenig eleganter, die frage ist, ob noch ein fall fehlt...

DJ_Freeman

Wenn du alle Überschneidungen willst, dann fehlt dir doch noch folgende:

4. Der neue Eintrag beginnt vor Start und endet nach Ende.

Wobei, wenn ich das grad richtig sehe, die letzte gepostete SQL-Anweisung nicht genau den beschriebenen Fall 2 und 3 präsentiert.

Wie wäre es damit:

Code:
SELECT id 
FROM tab 
WHERE 
  NOT 
  (
    (start_neu < start_alt AND ende_neu < start_alt) 
    OR 
    (start_neu > ende_alt AND ende_neu > ende_alt)
  )

Im Grunde prüfst du damit, ob der neue Zeitraum außerhalb des alten liegt und selektierst alle anderen Zeilen. Was "nicht außerhalb" (in SQL: entweder davor oder dahinter) liegt, muss demnach sich überschneiden.

Noch einfacher geht's nicht.
 
also wenn ihr wirklich alle fälle wollt warum dann net gleich
Code:
WHERE a.start != b.start AND a.ende != b.ende
dann sind alle fälle abgedeckt wo der timestamp nicht exackt dem anderen entspricht == überschneidung

oder hab ich was net verstanden

ps wenn ich mir deinen ersten Post ansehen würde ich die Where weglassen weil dann trifft alles zu:-? bzw nimm dann eine where ala

Code:
WHERE start IS NOT NULL AND ende IS NOT NULL

weil nach deinen ersten Post zu folge sind dort nur ID´s vorhanden bei denen das zutrifft wenn du alle bedingungen abdecken willst
 
Zuletzt bearbeitet:
Ich glaube, es geht hier nicht nur um exakte Überschneidung, sondern auch um teilweise beziehungsweise vollständige Überlagerung.

Und genau diese Fälle werden auch durch mein Query abgedeckt. Der neu hinzugekommene vierte Fall ist auch kein neuer, da er duch meine beiden angeführten Fälle 2 und 3 abgedeckt wird.

Wobei ich hingegen den ersten Fall total falsch aufgeschrieben habe. Denn laut meinen ersten angedachten Query würde sich der erste Fall durch die beiden anderen ergeben, aber da der erste Term in der WHERE-Klausel falsch war, sind nicht alle Fälle abgedeckt.

Ich habe die 4 Fälle von Überlagerung mal aufgezeichnet:



  1. Der neue Termin beginnt vor Start und endet vor Ende.
  2. Der neue Termin beginnt nach Start und endet vor Ende.
  3. Der neue Termin beginnt nach Start und endet nach Ende.
  4. Der neue Termin beginnt vor Start und endet nach Ende.
Meine Query hatte alle Fälle bis auf den hier vorgestellten Fall 2 abgedeckt.

Eine alles abdeckende Query wäre somit wohl:
Code:
SELECT id
FROM table
WHERE ([COLOR="Blue"]start BETWEEN {$start} AND {$ende}[/COLOR])
  OR ([COLOR="DarkRed"]ende BETWEEN {$start} AND {$ende}[/COLOR])
  OR ([COLOR="Green"]{$start} BETWEEN start AND ende[/COLOR])
Wie man erkennen kann, wird Fall 4 sowohl von Fall 1 als auch von Fall 3 abgedeckt.

SpecialsGuys Query müsste allerdings auch hinhauen, wobei ich grad ein wenig unsicher bin, da man durch Negation meiner Query eigentlich auf seine Query schliessen müsste, aber da hapert's grad bei mir. Immerhin sollen die gleichen Ergebnismengen zurückgeliefert werden, insofern muss der bool'sche Ausdruck den gleichen Wahrheitswert haben bzw die Aussage äquivalent sein. Um das jetzt aber rauszutüfteln, fehlt mir der Elan und die nötige Menge an Kippen. ;)
 
Zuletzt bearbeitet:
@tleilax: Ja, deine neue Query ist richtig und deckt auch alle Fälle ab, die alte allerdings hat dies allerdings nicht.

Meine Query sucht die zwei Fälle raus, in denen es keine Überschneidung gibt. Logisch gesehen ist es also die Negation von deiner, sql-mässig nicht, da ein "NOT BETWEEN" nicht alle Fälle wiederum abdecken würde mit nur zwei WHERE-Klauseln. Welche der beiden Querys nun schneller ist, müsste man testen, aber ich kann beide empfehlen :)

Tipp: In manchen Fällen ist es jedenfalls einfacher eine SQL-Anweisung zu konstruieren die nach dem "NOT (nicht-zutreffendes)" sucht, besonders in manch kompliziertem Fall (z.B. wenn auch JOIN im Spiel ist) lässt sich damit eine viel kleinere und effizientere Anweisung basteln.

Die von strolch allerdings ist falsch.
 
Jetzt, wo ich mir Deine Lösung nochmal anschaue, fällt mir noch eine weitere Lösung ein. Müsste man nicht einfach nur auf zwei Bedingungen prüfen:
  1. Entweder ist der neue Start hinter dem alten Ende.
  2. Oder das neue Ende ist vor dem alten Start.
Alle anderen Bedingungen würden zu Überschneidungen führen oder die grundlegenden Bedigungen der Query wären falsch (Ende vor Start).

Also, um die Felder mit den Überschneidungen zu finden:
Code:
SELECT id
FROM tab
WHERE NOT ( {$start} > ende OR {$ende} < start )
Wobei mir grad noch auffällt, dass es im Grundprinzip Deiner Lösung entspricht, aber nur auf den jeweils näher an den Grenzen liegenden Wert prüft.

So langsam nähern wir uns wohl dem Optimum... ;)

Achja, was ich die ganze Zeit noch zu einem Punkt weiter oben einwerfen wollte:

Hier an dieser Stelle (bei Zahlenvergleichen) sind die Ticks ( ' ) vollkommen unangebracht, da es dazu führen könnte, dass nicht numerisch ( 2 < 11 ), sondern lexikalisch verglichen wird ( '2' > '11' ). Sowas sollte man immer im Hinterkopf haben.
 
Und irgendwas gefiel mir schon die ganze Zeit nicht :D
Ja, du hast das Nonplus-Ultra-Optimum. Letzte Lösung zu empfehlen :)
Ist unser beider Verdienst :LOL:
 
Jo, danke euch beiden. Das letzte von tleilax scheint wirklich das ultimative Query zu sein.. Soweit ich das getestet hab klappts auch soweit :)

DJ_Freeman