André Laugks: mySQL: GROUP BY Problem

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

  1. 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

    1. 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

      1. 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