Jörg: mysql WHERE und IN?

Hallo,

wenn ich eine Query habe ala:

SELECT a,b,c FROM table WHERE ID IN (321,333,437) ,

diese IDs aber mehrfach in der Tabelle vorkommen, wie kann ich dann sicherstellen, dass nur jeweils der letzte Eintrag dieser IDs hier zum tragen kommt?

Jörg

  1. Tach!

    [...] wie kann ich dann sicherstellen, dass nur jeweils der letzte Eintrag dieser IDs hier zum tragen kommt?

    Die Daten in einer Tabelle sind per Definiton unsortiert. Wenn du den ersten/letzten haben möchtest, musst du zunächst ein Sortierkriterium festlegen. Nach dem Sortieren kannst du mit LIMIT die Ergebnismenge von vorn einschränken, oder von hinten bei entgegengesetzter Sortierung.

    dedlfix.

    1. Hi dedlfix,

      Die Daten in einer Tabelle sind per Definiton unsortiert. Wenn du den ersten/letzten haben möchtest, musst du zunächst ein Sortierkriterium festlegen. Nach dem Sortieren kannst du mit LIMIT die Ergebnismenge von vorn einschränken, oder von hinten bei entgegengesetzter Sortierung.

      Benötige ich hierzu FIELD oder FIND_IN_SET ? Oder muss ich eine Subquery hinter das IN packen? Mir ist nicht ganz klar, wie ich formuliere, dass ich jeweils den letzten Datensatz benötige.

      Jörg

      1. Hallo Jörg,

        wenn man so im Netz rumsucht, findet man Lösungen mit GROUP_CONCAT und FIND_IN_SET. Da ich kein geborener MySQLer bin, sondern von DB2 und MS SQL komme, ist das für mich fremdes Kauderwelsch. Aber möglicherweise kommst Du auch damit weiter.

        Mein Vorgehen in dem Fall kennst Du, das hatten wir neulich mit dem Datums-Subselect.

        Hier kannst Du ähnlich vorgehen.

        Versuche, eine Query zu formulieren, die Dir pro ID das Kriterium für den "letzten Eintrag" findet. Wenn Du Dinge "pro Schlüsselbegriff" tun willst, sollte bei Dir automatisch das "GROUP BY" Glöckchen klingeln. Wenn Du pro ID das jüngste Datum haben willst, wäre es sowas:

        SELECT id, MAX(datum) FROM table WHERE ID IN (4,7,11) GROUP BY id
        

        Und da joinst Du table wieder dazu, so wie im vorigen Thread auch.

        SELECT t2.id, t2.datum, t2.dings, t2.bums, ...
        FROM (SELECT id, MAX(datum) as maxdat 
              FROM table 
              WHERE ID IN (4,7,11)
              GROUP BY id) t1
           JOIN
             table t2
           ON t1.id = t2.id AND t1.maxdat = t2.datum
        

        Das "GROUP BY" bewirkt, dass die Maximumsuche nur innerhalb einer ID durchgeführt wird.

        Die id in (4,7,11) Abfrage kannst Du in die innere Query packen oder als WHERE an die äußere Query hängen, ich würde nur mutmaßen, dass es schneller ist, die richtigen IDs vor dem Join herauszusuchen statt erstmal alles zu JOINen und dann zu filtern. Aber möglicherweise optimiert MySQL das ohnehin.

        Es ist hier so, dass MAX(datum) die einzige Spalte ist, die Du als Vergleichswert brauchst. Deswegen geht es auch per Subselect und ist vermutlich auch übersichtlicher:

        SELECT t2.id, t2.datum, t2.dings, t2.bums, ...
        FROM table t2 
        WHERE t2.id in (4,7,11)
          AND t2.datum = (SELECT MAX(datum) 
                          FROM table t1 
                          WHERE t1.id = t2.id)
        

        Das Thema „wie finde ich die zwei jüngsten Einträge“ ist allerdings komplexer. Hier wird das ausführlich abgehandelt (auf englisch).

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          Es ist hier so, dass MAX(datum) die einzige Spalte ist, die Du als Vergleichswert brauchst. Deswegen geht es auch per Subselect und ist vermutlich auch übersichtlicher:

          Absolut, weshalb ich mich auch gleich spontan dafür entschieden habe.

          SELECT t2.id, t2.datum, t2.dings, t2.bums, ...
          FROM table t2 
          WHERE t2.id in (4,7,11)
            AND t2.datum = (SELECT MAX(datum) 
                            FROM table t1 
                            WHERE t1.id = t2.id)
          

          Ich brauche zwar in diesem Fall keine 2. Tabelle, weil das Datum in derselben Tabelle steht, wie die restlichen Angaben, aber es macht natürlich keinen Unterschied, weil der vergleich ja benötigt wird. Insofern dann den bezug eben wieder zur tabelle 1.

          Das Thema „wie finde ich die zwei jüngsten Einträge“ ist allerdings komplexer. Hier wird das ausführlich abgehandelt (auf englisch).

          Dann bin ich mal froh, dass ich nur den letzt jüngsten Datensatz benötige. Btw., der Link funktioniert nicht.

          Danke abermals für Deine Hilfe, Lösung2 funktioniert bei mir pefekt.

          Jörg

          1. Tach!

            SELECT t2.id, t2.datum, t2.dings, t2.bums, ...
            FROM table t2 
            WHERE t2.id in (4,7,11)
              AND t2.datum = (SELECT MAX(datum) 
                              FROM table t1 
                              WHERE t1.id = t2.id)
            

            Ich brauche zwar in diesem Fall keine 2. Tabelle, [...]

            Du brauchst aber zwei Aliase, weil das zwei Datenmengen sind. Zumindest die äußere Query braucht eins, weil du dich im Subselect auf die äußere Datenmenge eindeutig beziehen musst.

            dedlfix.

            1. Hi dedlfix,

              Du brauchst aber zwei Aliase, weil das zwei Datenmengen sind. Zumindest die äußere Query braucht eins, weil du dich im Subselect auf die äußere Datenmenge eindeutig beziehen musst.

              Genau, habe ich ja auch geschrieben. 😀 Dir auch danke und Gruß, Jörg

          2. Hallo Jörg,

            Btw., der Link funktioniert nicht.

            Nanü? Bei mir geht er. Soeben nochmal klickgetestet. Hier die Version ohne Alternativtext:

            https://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hallo Rolf,

              Nanü? Bei mir geht er. Soeben nochmal klickgetestet. Hier die Version ohne Alternativtext:

              Jetzt geht bei mir der Link auch in beiden Versionen, ging vorhin aber definitiv nicht.

              Jörg

      2. Tach!

        Mir ist nicht ganz klar, wie ich formuliere, dass ich jeweils den letzten Datensatz benötige.

        "Jeweils" wovon? Meines Wissens kannst du zum Beispiel keine Gruppen bilden und von der Gruppe nur bestimmte Datensätze wählen. Also jedenfalls nicht direkt. Aber es gibt Window Functions, die ähnlich wie GROUP BY funktionieren. Im Gegensatz zu GROUP BY bleiben die Datensätze aber erhalten und werden nicht zu einer Zeile je Gruppe zusammengefasst. Aber man kann der Ergebnismenge zu jeder Zeile Aggregatewerte hinzufügen, die über eine Gruppe gebildet werden können. Siehe verlinkte Seite der country_profit im dritten Code-Block.

        Du könntest in einem ersten Schritt mit MAX()/MIN() die Extremwerte einer Gruppe ermitteln und hinzufügen. Dan kannst du den Wert vom Datensatz mit dem Extremwert vergleichen und damit die anderen rausfiltern. Leider kommt jedoch HAVING vor der Window-Berechnung, so dass du dafür einen zweiten Schritt brauchst. Also das beschriebene Gebilde in eine Subquery und in der Hauptquery das Filtern einbauen.

        Benötige ich hierzu FIELD oder FIND_IN_SET ?

        Beide Funktionen arbeiten gemäß ihrer Beschreibung nicht mit Ergebnismengen.

        dedlfix.

  2. Hallo,

    so nebenbei:

    ich habe mir angewöhnt, Schlüssel, die in einer Spalte mehrfach vorkommen können, nicht mehr mit "ID" zu bezeichnen, sondern mit "REF" oder "REL" oder noch besser gleich mit "REF_table_col", wobei table und col dann für die Tabelle (und die Spalte) stehen, auf die sich die Referenz/Relation bezieht.

    ID steht dann nur noch für einen lost unique key, wird also nur einmal und nie wieder vergeben, auch nicht, wenn der Datensatz gelöscht wurde.

    Im Gegensatz zur weit verbreiteten Meinung kann man diesen Key bei den meisten DBMS auch streng inkrementell definieren, sodass man gleich eine zeitliche Abfolge der Inserts daraus ableiten kann.

    LG
    Ralf