GROUP BY

Alles, was MariaDB und MySQL betrifft, kann hier besprochen werden.

GROUP BY

Postby startist » 17. November 2008 14:17

Hallo,

folgendes Problem: In einer DB stehen verschiedene Datensätze, die teilweise gleiche Werte in einem Feld namens "PNr" besitzen. Bei einer bestimmten Abfrage möchte ich nach dieser PNr gruppieren. Nur soll MySQL nicht irgendeinen der betreffenden Datensätze zurückgeben, sondern den mit dem höchsten Datum, welches in einem anderen Feld steht.
Gibt es eine Möglichkeit, dem "GROUP BY"-Befehl mitzuteilen, welchen Datensatz er aus den Möglichen ausliest?

Danke,
startist
User avatar
startist
 
Posts: 55
Joined: 24. June 2008 14:10
Location: Dresden

Postby Stepke-DSL » 17. November 2008 14:41

Hallo,

Sortier doch das Ergebnis nach dem Datumsfeld, oder darf nur ein Datensatz zurückgegeben werden?

MfG Stepke
Stepke-DSL
 
Posts: 313
Joined: 12. July 2007 16:08
Operating System: Win XP Pro SP3 + SuSe Linux

Postby startist » 17. November 2008 15:01

Sortier doch das Ergebnis nach dem Datumsfeld


Du meinst, ich soll das Ergebnis erst sortieren (nach Datum) und danach gruppieren? Das setzt aber vorraus, dass MySQL immer den ersten betreffenden Datensatz zurückgibt. Ich habe aber den Eindruck, dass MySQL bei GROUP BY willkürlich auswählt, oder täusche ich mich da?

oder darf nur ein Datensatz zurückgegeben werden


Nein, es werden eine ganze Menge Datensätze zurückgegeben, aber welchen Unterschied macht das?

Grüße,
startist
User avatar
startist
 
Posts: 55
Joined: 24. June 2008 14:10
Location: Dresden

Postby Stepke-DSL » 17. November 2008 15:07

Versuch es mal indem du erst gruppierst und dann sortieren.

Beim gruppieren werden ja gleiche Inhalte zusammengefasst und diese zusammengefassten (gruppierten) Inhalte müssen dann nur noch so sortiert werden, dass das aktuellste Datum an erster Stelle steht.

MfG Stepke
Stepke-DSL
 
Posts: 313
Joined: 12. July 2007 16:08
Operating System: Win XP Pro SP3 + SuSe Linux

Postby Nobbie » 17. November 2008 17:00

SELECT PNr, blabla, ..., Datum
FROM tabelle t1
WHERE Datum=(SELECT MAX(t2.Datum)
FROM tabelle t2
WHERE t1.PNr = t2.PNr);
Nobbie
 
Posts: 13170
Joined: 09. March 2008 13:04

Postby startist » 18. November 2008 15:03

Also, ich kapiers net :oops:

So sieht meine Abfrage derzeit aus, die zwar nach b.los_id gruppiert, aber eben zufällig einen der betreffenden Datensätze zurückgibt:

Code: Select all
            SELECT
               b.data_id,
               b.los_id,
               p.erfasst,
               p.eingang,
               p.methode_id,
               v.meth_name,
               m.familie_id,
               f.fam_name,
               p.charge,
               p.material_id,
               m.mat_name,
               m.kurztext,
               p.ber_nr,
               p.lab_nr,
               p.info,
               p.farb_msg,
               p.status_id,
               s.status,
               p.notiz,
               p.dauer,
               b.traeger,
               b.engine_id,
               e.pruef_nr,
               b.start,
               b.std,
               b.dl,
               b.da,
               b.db,
               b.de,
               b.stufea02_id,
               a.a02_name,
               b.urteil,
               b.beo,
               b.datum,
               b.papierkorb
            FROM berichte b
            LEFT JOIN lose p
            ON (b.los_id = p.probe_id)
            LEFT JOIN methode v
            ON (p.methode_id = v.meth_id)
            LEFT JOIN status s
            ON (p.status_id = s.status_id)
            LEFT JOIN material m
            ON (p.material_id = m.mat_id)
            LEFT JOIN familie f
            ON (m.familie_id = f.fam_id)
            LEFT JOIN geraet e
            ON (b.engine_id = e.engine_id)
            LEFT JOIN a02 a
            ON (a.a02_id = b.stufea02_id)
            WHERE b.urteil='i.O.'
            AND b.papierkorb=0
            GROUP BY b.los_id
            ORDER BY b.data_id ASC
            LIMIT 0, 30;


Wie bekomme ich nun die Datensätze mit dem jeweils aktuellsten Datum? Ich habe mit Aggregatfunktionen herumprobiert, aber diese habe ich noch nicht verinnerlicht und bekomme nur Müll und/oder Fehlermeldungen zurück :?

Grüße, startist
User avatar
startist
 
Posts: 55
Joined: 24. June 2008 14:10
Location: Dresden

Postby Nobbie » 18. November 2008 15:24

Wo ist denn das Feld PNr?
Nobbie
 
Posts: 13170
Joined: 09. March 2008 13:04

Postby startist » 18. November 2008 16:59

PNr ist b.los_id

der Einfachheit halber nannte ich es PNr
User avatar
startist
 
Posts: 55
Joined: 24. June 2008 14:10
Location: Dresden

Postby Nobbie » 18. November 2008 21:05

Und was ist das für eine Software?
Nobbie
 
Posts: 13170
Joined: 09. March 2008 13:04

Postby Wiedmann » 18. November 2008 21:17

Wie bekomme ich nun die Datensätze mit dem jeweils aktuellsten Datum?

Du benutzt noch "MAX(b.data_id) AS sortdatum" und sortierst nach "sortdatum".
Wiedmann
AF Moderator
 
Posts: 17102
Joined: 01. February 2004 12:38
Location: Stuttgart / Germany

Postby Nobbie » 19. November 2008 12:31

Das geht so oder so nicht, denn es ist semantisch falsch, eine Gruppierung vozunehmen und gleichzeitig weitere Tabellenfelder zu selektieren. Bei einer Gruppierung kann nur das gruppierte Feld und sog. "Aggregatfunktionen" (wie MAX(), SUM(), MIN() usw.) anderer Felder selektiert werden.

MySQL läßt diese fehlerhafte Selektierung zwar passieren, aber liefert unvorhersehbare Werte zurück. Bei anderen SQL Systemen (Oracle beispielsweise) würde der Select gar nicht erst ausgeführt werden, sondern mit entsprechender Fehlermeldung abgebrochen werden.

Zwei Lösungsansätze:

1) Du machst den GROUP BY b.los_id und selektiert ausschließlich MAX(b.data_id) dazu. Das ganze selektierst Du in eine temporäre Tabelle und anschließend selektierst Du in einem weiteren Select die Felder wie gewünscht und ergänzt die FROM bzw. WHERE-Klausel dahingehend, dass Du mit der temporären Tabelle joinst und nach los_id und den ermittelten max-data.id suchst.

2) Du führst erst gar keine Gruppierung durch, sondern sortierst (ORDER BY b.los_id ASC, b.data_id DESC) die Ausgabe nach b.los_id aufsteigend und innerhalb gleicher los_ids absteigend nach b.data_id (siehe vorne). Dies ergibt eine Tabelle mit sog. Gruppenwechsel nach b.los_id, d.h. wenn Du diese Fundstelle zeilenweise ausliest (via mysql_fetch_xxx) überprüfst Du in der Leseschleife den Wert von b.los_id und bei jedem Wechsel nimmst Du nur jeweils den allerersten Datensatz mit dieser los_id - das ist der Datensatz mit dem höchsten Datum. Die restlichen Sätze mit gleicher los_id ignorierst Du in der Verarbeitung.
Nobbie
 
Posts: 13170
Joined: 09. March 2008 13:04

Postby Wiedmann » 19. November 2008 12:44

aber liefert unvorhersehbare Werte zurück.

Eigentlich liefert MySQL genau und immer das gewünschte zurück. Liegt aber wohl daran, dass dies genauso im MySQL-Manual beschrieben ist (unabhängig davon, oder andere System dann diese Extension nicht unterstützen).

Ansonsten gibt es bei aktuellen MySQL-Versionen (oder anderen Systemen) noch die Lösung, dass man die eigentliche Abfrage mit sortiereb in einem subselect im FROM macht, und das Ergebnis dann gruppiert.
Wiedmann
AF Moderator
 
Posts: 17102
Joined: 01. February 2004 12:38
Location: Stuttgart / Germany

Postby Nobbie » 19. November 2008 12:55

Wiedmann wrote:Eigentlich liefert MySQL genau und immer das gewünschte zurück. Liegt aber wohl daran, dass dies genauso im MySQL-Manual beschrieben ist (unabhängig davon, oder andere System dann diese Extension nicht unterstützen).


Nein, Du verstehst das Problem nicht. Einfaches Beispiel, Tabelle mit 2 Feldern A und B:

A B
====
1 a
1 b
1 c
2 x
2 y
2 z


Und nun selektieren wir wie folgt:

SELECT A, B FROM tabelle GROUP BY A

Was soll denn das (vorhersehbare!) Ergebnis dieses Selects sein? Das ist Käse, weil der Wert von B nicht bestimmt ist. Da nützt auch eine Sortierung nichts:

SELECT A,B FROM tabelle GROUP BY A ORDER BY B

Das ist immer noch sinnlos, denn die Sortierung wird nach(!) der Gruppierung durchgeführt.

Und deswegen ist das semantisch falsch - und auch MySQL gibt "unpredictable results" zurück. Da finde ich ANSI SQL schon besser, dann da geht die Abfrage gar nicht erst durch.
Nobbie
 
Posts: 13170
Joined: 09. March 2008 13:04

Postby startist » 19. November 2008 15:10

Danke erstmal für eure rege Beteiligung.
Und was ist das für eine Software?

In ein paar Worten ist das schwer zu erklären, aber ich versuche es dennoch: Es gibt Produktionsartikel, welche umfangreichen Tests (für Qualitätszwecke) ausgesetzt werden. Diese bekommen eine Nummer (die los_id) und werden in einer Tabelle gespeichert. In einem bestimmten Zyklus werden die Tests durchgeführt. Die Testergebnisse (u.a. eine allgemeine Beurteilung) werden dann unter einer laufenden Nummer (data_id) und dem aktuellen Datum in der Tabelle berichte gespeichert. Anfangs haben die Tests meist noch das Prädikat "i.O.". Nach einigen Tests (bspw. beim fünften Mal) ist das Ergebnis nicht mehr zufriedenstellend und bekommt das Prädikat "NIO".

Die Abfrage, um die es hier geht, dient zu Auswertungszwecken. Dazu soll von jedem Artikel nur das Testergebnis ausgegeben werden, welches als letztes noch das Prädikat "i.O." bekommen hat (dass in der Spalte urteil steht).

Ich habe dafür auch schon eine Lösung programmiert, indem ich die Abfrage ohne GROUP BY vornehme und ohne die Anzahl der Datensätze zu limitieren. Diese Datensätze werden dann in ein Array übertragen, welches ich dann mittels PHP abarbeite. Hier ist es kein Problem das aktuellste Datum eines Artikels mit dem Prädikat "i.O." herauszufiltern.

Jetzt habe ich aber die Befürchtung, dass bei einigen Tausend Einträgen die Software sehr langsam werden könnte. Mir fehlt da eben ein wenig die Erfahrung. Deswegen wollte ich es über MySQL probieren. Die Software läuft ausschließlich in einem Firmennetzwerk und wird von höchstens 1-3 Mitarbeitern (im schlimmsten Fall vielleicht 5) benutzt. Der Rechner, auf dem der Webserver läuft ist aktuell und sehr gut gerüstet.

Ich habe also auf GROUP BY gesetzt in der Hoffnung, MySQL mitteilen zu können, unter welchen Bedingungen zu gruppieren ist. MySQL nimmt bei GROUP BY einen willkürlichen der betreffenden Datensätze. Ich war guter Dinge, dass ich MySQL eben noch irgendwie sagen kann, nimm nicht irgendeinen, sondern den mit dem höchsten Datum. Aber so einfach ist es dann wohl doch nicht :cry:

So, jetzt kennt ihr die Hintergründe und meine Gedanken dazu. Vielleicht lohnt es sich gar nicht, die SQL-Abfrage zu optimieren und weiterhin lieber auf die Filterung durch PHP zu setzen?

Gruß, startist
User avatar
startist
 
Posts: 55
Joined: 24. June 2008 14:10
Location: Dresden


Return to MariaDB - MySQL

Who is online

Users browsing this forum: No registered users and 16 guests