mySQL: GROUP BY Problem
André Laugks
- datenbank
Hallo!
Ich habe eine Tabelle,
// foto
fotosatz_id person_id rang
10 2 1
143 2 2
567 2 3
Folgender Datensatz wurde als erster für die Person (person_id=2) in die Tabelle foto eingetragen.
fotosatz_id person_id rang
10 2 1
"SELECT fotosatz_id FROM foto WHERE person_id=2 GROUP BY person_id ORDER BY rang ASC;" liefert mir folgenden erwarteten Datensatz.
fotosatz_id person_id rang
10 2 1
Nun ändere ich aber die Rangfolge:
fotosatz_id person_id rang
10 2 3
143 2 2
567 2 1
Nun setzte ich das selben SQL-Statement ein und bekomme aber nicht den erwarteten Daten, also diesen:
fotosatz_id person_id rang
10 2 3
Ich erwarte aber folgenden Datensatz:
fotosatz_id person_id rang
567 2 1
Mir wird also der zuerst eingetragene Datensatz angezeigt, was bei jedem Datensatz zu einer Person so ist.
Danke und MfG, André Laugks
Hi André
"SELECT fotosatz_id FROM foto WHERE person_id=2 GROUP BY person_id ORDER BY rang ASC;" liefert mir folgenden erwarteten Datensatz.
Dass kann so nicht funktionieren. Mit dem group by sagst du ihm sozusagen, ich will alle Datensätze für eine person_id zu einer einzigen zusammengefasst haben und da es nur noch eine Zeile ist, kann der order by nicht mehr viel bewirken. Er sortiert sozusagen die eine noch verbleibende Zeile. Welche Zeile genommen wird, ist zufällig (im Normalfall eben die erste, aber das ist auch nicht verlässlich).
Was du nun brauchst, sind Gruppenfunktionen, in deinem Fall min und max.
Die Query für min sähe nun so aus:
SELECT fotosatz_id, min(rang)
FROM foto
WHERE person_id=2
GROUP BY person_id
Für die Grösste dann einfach min durch max ersetzen.
Gruss
Daniela
Hallo Daniela
"SELECT fotosatz_id FROM foto WHERE person_id=2 GROUP BY person_id ORDER BY rang ASC;" liefert mir folgenden erwarteten Datensatz.
kann der order by nicht mehr viel bewirken. Er sortiert sozusagen die eine noch verbleibende Zeile.
Ich dachte mir es so, das die Gruppe nach der Sortieranweisung gebildet wird, dann also der Datensatz angezeigt wird, der der Sortierung entspricht.
Die Query für min sähe nun so aus:
SELECT fotosatz_id, min(rang)
FROM foto
WHERE person_id=2
GROUP BY person_id
Für die Grösste dann einfach min durch max ersetzen.
fotosatz_id person_id rang
10 2 3
143 2 2
567 2 1
Mit Deinem SQL-Statement wird mir nun folgendes ausgegeben:
// person_id habe ich mit hinzugefügt
fotosatz_id person_id rang
10 2 1
Es wird mir also der zuerst eingefügte Datensatz angezeigt, mit dem kleinsten rang für person_id=2. Die fotosatz_id steht hier für die Nummer eines Bildes, ich würde also das falsche Bild angezeigt bekommen.
Mit
SELECT fotosatz_id, person_id
FROM foto
WHERE person_id=2 AND rang=1;
würde ich natürlich die richtige Fotonummer bekommen. Dies ist mir aber zu unflexibel. Kann man den den kleinsten gefundenen rang an den Datensatz binden, zudem er gehört? Also die "1" gehört zu 567..., ich konnte dazu jetzt nichts finden.
Zur Zeit sichere ich durch das "Fotosortiertool" ab, daß es einen "Rang 1" gibt und aufsteigend, wenn also die Fotos sortiert werden.
MfG, André Laugks
Hi,
Ich dachte mir es so, das die Gruppe nach der Sortieranweisung gebildet wird, dann also der Datensatz angezeigt wird, der der Sortierung entspricht.
nein - es wird erst GROUP BY ausgeführt, was zu n Zeilen bei n unterschiedlichen person_id führt.
Oracle würde bei Deinem Query übrigens tüchtig meckern, weil es eben keinen Sinn ergibt - wenn Du die fotosatz_id haben willst, mußt Du danach auch gruppieren. Andernfalls kann sie nicht sinnvoll ermittelt werden.
Daß MySQL nichts dagegen hat würde ich als Bug bezeichnen; mindestens aber als Problem, auf das Du soeben gestoßen bist.
Mit Deinem SQL-Statement wird mir nun folgendes ausgegeben:
// person_id habe ich mit hinzugefügt
fotosatz_id person_id rang
10 2 1
Ja, das ist "logisch". Er hat nach person_id gruppiert, also quasi Untertabellen ermittelt; aus jeder einzelnen den min(rang) ermittelt; und diesen ausgegeben. Daß Du noch die fotosatz_id haben möchtest ist wie gesagt ein Denkfehler von Dir - das _kann_ nicht funktionieren.
Kann man den den kleinsten gefundenen rang an den Datensatz binden, zudem er gehört?
MySQL 4.0 wird voraussichtlich Subselects beherrschen. Bis dahin - sorry - wirst Du mit den Konsequenzen Deiner DBMS-Wahl leben müssen. Ob es ein einzelnes Statement ohne Subselects gibt, das Deinen Ansprüchen genügt, weiß ich nicht; vermutlich wirst Du das Problem über Programmlogik lösen müssen.
Mit Subselects:
SELECT fotosatz_id, person_id, rang
FROM foto
WHERE person_id||'.'||rang in (
SELECT person_id||'.'||min(rang)
FROM foto
GROUP BY person_id
)
Ohne Gewähr, möglicherweise nicht das Optimum, vermutlich mit DBMS-spezifischer Syntax.
Cheatah