Hallo Linuchs,
das ist fast richtig. Aus welcher Row Du den Wert einer Spalte bekommst, die nicht im GROUP BY steht, ist nicht zufällig, sondern undefiniert. D.h. du wirst schon konsistent immer den gleichen Wert bekommen. Aber es gibt kein Sprachkonstrukt, das Dir die Row garantiert. VERMUTLICH wirst Du den richtigen Wert bekommen, wenn Du die Table mit einem Cluster-Index auf address_id, tag und uhr anlegst, weil die Rows dann genau so gespeichert sind, dass die MIN-Row die erste ist die er antrifft. Es gibt aber keine Garantie.
Angesichts der Tatsache, dass diese Frage so unglaublich oft gestellt wird, sollte man doch meinen, dass die SQL Designer dafür ein Sprachkonstrukt anbieten würden. Aber nein.
Man sollte auch meinen, dass es eine Tupel-Version von MIN gäbe, so dass man MIN(tag, uhr) schreiben kann und am kleinsten Tag die kleinste Uhrzeit bekäme. Aber nein.
Klassische Lösung: Self-Join vom GROUP-Konstrukt mit der Tabelle:
SELECT trm1.min_tag_uhr, trm2.ort
FROM (SELECT adress_id, MIN( CONCAT( tag, ' ', uhr )) AS min_tag_uhr
FROM termine
GROUP BY adress_id) trm1
JOIN termine trm2
ON trm1.adress_id = trm2.adress_id
AND trm1.min_tag_uhr = CONCAT(trm2.tag, ' ', trm2.uhr)
Dedlfix würde den Selfjoin vermutlich sofort durch einen Subselect ersetzen. Ich glaube, das sähe dann so aus (ungetestet):
SELECT trm1.adress_id, trm1.ort, trm1.tag, trm1.uhr
FROM termine trm1
WHERE CONCAT(trm1.tag, trm1.uhr) =
( SELECT MIN(CONCAT(trm1.tag, trm1.uhr))
FROM termine trm2
WHERE trm1.adress_id=trm2.adress )
Vielleicht auch so:
SELECT trm1.adress_id,
trm1.nächster,
(SELECT trm2.ort FROM termine trm2
WHERE trm2.adress_id = trm1.adress_id
AND CONCAT(trm2.tag, ' ', trm2.uhr) = trm1.nächster) as ort
FROM (SELECT adress_id, MIN(CONCAT(tag, ' ', uhr)) as nächster,
FROM termine trm1
GROUP BY adress_id) trm1
ORDER BY trm1.nächster
Allerdings ist das alles angesichts des CONCAT nicht gerade effizient. Du solltest je nach Query-Aufkommen überlegen, ob Du für tag_uhr nicht eine persistente, berechnete Spalte hinzufügst und nach der auch indexierst, denn wenn du tausende von Zeilen hast, ist das eine ziemliche Table-Scannerei.
Moderne Lösung: Wenn Du MYSQL 8 oder eine aktuelle MariaDB hast, kannst Du auch Window-Funktionen benutzen. Die gibt's im MYSQL 5 nicht.
SELECT adress_id,
ort, tag, uhr,
ROW_NUMBER() OVER(PARTITION BY adress_id
ORDER BY tag, uhr) as term_row
FROM termine
WHERE term_row = 1
ORDER BY tag, uhr
Müsste zumindest so oder so ähnlich aussehen. Ich habe Windowfunktionen noch nicht selbst gebraucht und müsste erstmal Tests machen, um Funktionsgarantie zu geben. Aber Handbuchlesen kannst Du sicher auch selbst. Und vermutlich kannst Du den ROW_NUMBER Ausdruck auch direkt ins WHERE schreiben und brauchst ihn nicht in der SELECT Liste.
Wichtig ist, dass Du ROW_NUMBER nimmst und nicht RANK. Zum einen dürfte ROW_NUMBER weniger aufwändig sein, zum anderen liefert RANK bei gleichen Werten auch den gleichen Rang. Was bei Dir vermutlich nicht vorkommt.
Aber verstehe ich das richtig, dass Du diese Abfrage übergreifend über mehrere Veranstalter hast? Wenn Du sie nur für einen Veranstalter machst, ist es einfacher, da reicht ein ORDER BY tag, uhr LIMIT 1.
Rolf
sumpsi - posui - obstruxi