Jörg: mysql, Query, Immer nur der letzte Eintrag

Hallo,

ich habe 2 Tabellen:

1: ID1,Spalte1
2: ID2,ID1,Datum

Und meine Query ist:

SELECT 
t1.ID11,
t1.Spalte1,
t2.ID2
FROM table1 t1 
LEFT JOIN table2 t2 ON t1.ID1 = t2.ID1

So erhalte ich alle Einträge aus table1 und zusätzlich auch die ID2, falls hierzu ein Eintrag in table2 vorhanden ist.

Der kleine Sc hönheitsfehler ist, dass ich auch 5 Einträge aus table2 erhalte, falls 5 Einträge vorhanden sind. Mir würde aber der vom Datum her jeweils jüngste Eintrag aus table2 ausreichen.

Wie mache ich das?

Jörg

  1. Hallo Jörg,

    das ist mühsam und hässlich. Und ich nehme an, du möchtest auch eine Spalte2 aus table2 haben, sonst kannst Du auf den Join verzichten 😉

    Du kannst mit einer Zusatzklausel den JOIN auf das jüngste Datum limitieren:

    SELECT t1.ID1, t1.Spalte1, t2.ID2, t2.Spalte2
    FROM table1 t1 
    LEFT JOIN table2 t2 ON t1.ID1 = t2.ID1 
                       AND t2.Datum = (SELECT MAX(Datum) 
                                       FROM table2
                                       WHERE id = t1.ID1)
    

    Keine Ahnung wie gut der SQL Server das optimiert, das muss man messen. Ein Index, der ID2 und Datum enthält, ist vermutlich hilfreich.

    Eine weitere Alternative wäre, die Query zu teilen. Zuerst nur table1 lesen, dann table2, mit einem ORDER BY nach Datum absteigend, und einem Limit auf den ersten Treffer. Das sind dann zwei Queries an die DB - mit Hilfe einer Routine (Stored Procedure) könnte man ggf. erreichen, dass man nur einen Roundtrip zum DB Server braucht. Aber das lohnt vermutlich nur, wenn der auf einem separaten Computer läuft.

    Die Alternative ist Postprocessing des Query-Ergebnisses in der Programmiersprache. Wenn es in table2 viele Sätze zu einer ID gibt, ist die MAX-Query vermutlich besser. Das lässt sich nur in deiner konkreten Umgebung beobachten und messen.

    Rolf

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

      Du kannst mit einer Zusatzklausel den JOIN auf das jüngste Datum limitieren:

      SELECT t1.ID1, t1.Spalte1, t2.ID2, t2.Spalte2
      FROM table1 t1 
      LEFT JOIN table2 t2 ON t1.ID1 = t2.ID1 
                         AND t2.Datum = (SELECT MAX(Datum) 
                                         FROM table2
                                         WHERE id = t1.ID1)
      

      Das habe ich versucht und es funktioniert, danke.

      Leider ergibt sich hieraus ein Problem, das ich nicht bedacht hatte:

      In table2 gibt es eine Flag-Spalte XYZ(int 1). Und es sollen alle Einträge mit XYZ=0 angezeigt werden, während von denen mit XYZ=1 nur der jeweils älteste angezeigt werden soll. Sorry für die weitere Einschränkung, die ist mir selber erst gerade beim Testen aufgefallen.

      Jörg

      1. Hallo Jörg,

        naja, dann musst Du

        • MIN(Datum) statt MAX(datum) verwenden, sonst kommt nicht der älteste
        • die MIN-Datum Bestimmung auf Sätze mit xyz=1 begrenzen
        • Alle sätze mit xyz=0 reinnehmen, unabhängig vom Datum.

        Das kriegst Du doch bestimmt hin. Gehört alles in das ON.

        ON (ids stimmen überein) AND ((Datum Stimmt) OR (xyz=0))

        Auf der linken Seite vom OR brauchst Du xyz=1 nicht abzufragen, das ist boolesche Optimierung. (A AND B) OR (NOT A) ist identisch zu B OR (NOT A). Mach Dir eine Wahrheitstabelle wenn Du es nicht glaubst 😀

        Rolf

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

          erstmal danke für die Hilfe und dann noch eine Korrektur, MAX() war schon korrekt, ich hatte vorhin fälschlich "älteste" geschrieben, gesucht wird aber nach wie vor der letzte, also jüngste Eintrag.

          ON (ids stimmen überein) AND ((Datum Stimmt) OR (xyz=0))

          Auf der linken Seite vom OR brauchst Du xyz=1 nicht abzufragen, das ist boolesche Optimierung. (A AND B) OR (NOT A) ist identisch zu B OR (NOT A). Mach Dir eine Wahrheitstabelle wenn Du es nicht glaubst 😀

          Da ich reiner Autodidakt bin und somit nicht per Studium verpflichtet war, habe ich noch nie eine Wahrheitstabelle angefertigt. Wenn ich nicht zufällig ein paar Informatikstudenten kennen würde, würde ich den Begriff gar nicht kennen. 😉

          Ich kann mir aber vorstellen, was Du meinst und glaube es Dir, trotzdem habe ich es jetzt mal zum besseren verständnis (für mich) ohne bolsche Optimierung in die Query aufgenommen, nur kommt als Ergebnis imm,er noch nicht genau das heraus, was ich suche.

          Bei folgenden Daten der table2 z.b.

          ID	XYZ	Date
          51	0   2021-04-01
          60	0	2021-04-01	
          62	1	2021-04-01
          63	1	2021-04-01
          63	0	2021-04-02
          
          

          verliere ich mit folgender Query

          ...
          AND (((t2.Datum = (SELECT MAX(Datum)
                                            FROM table2 t2
                                            WHERE t2.ID = t1.ID) AND XYZ = 1) OR (XYZ = 0)) AND t2.del = 0)
          

          den an 4.Stelle liegenden Datensatz aus der Ergebnismenge.

          Der soll zwar auch herausfallen, aber erst dann, wenn der an 5.Stelle liegende Datensatz mit derselben ID den XYZ-Flag 1 erhält.

          Vorher würde ich gerne beide in der Ergebnismenge haben.

          Danke für Deine Mühe + Hilfe und einen vorosterlichen Gruß,

          Jörg

          1. Hallo Jörg,

            tjaaa. Ich habe in meinem vorigen Posting ganz bewusst was fett gemacht. Und das hast Du nicht umgesetzt.

            Von wo bis wo geht der Subselect, der das Max-Datum bestimmt? Und was wird in dem Subselect nicht abgefragt? Eben 😉

            Kannst Du übrigens sicherstellen, dass es zu einer ID und einem Datum immer nur einen Satz gibt? Denn andernfalls ist der "jüngste" nicht klar definiert und Du kriegst ggf. mehr als einen mit xyz=1.

            Wahrheitstabellen sind aber was ganz simples. Wenn man prüfen will, ob man einen logischen Ausdruck korrekt optimiert hat, sind sie unverzichtbar. Damit solltest Du Dich befassen.

            Rolf

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

              tjaaa. Ich habe in meinem vorigen Posting ganz bewusst was fett gemacht. Und das hast Du nicht umgesetzt.

              Ich hatte mich schon gefragt, warum das fett ist. 😏

              Von wo bis wo geht der Subselect, der das Max-Datum bestimmt? Und was wird in dem Subselect nicht abgefragt? Eben 😉

              Ich weiß wirklich nicht, wo ichs hinpacken soll :-( Ist irgendwie, wie beim Ostereier suchen. Da war ich auch nie sonderlich gut drin 😉

              Kannst Du übrigens sicherstellen, dass es zu einer ID und einem Datum immer nur einen Satz gibt? Denn andernfalls ist der "jüngste" nicht klar definiert und Du kriegst ggf. mehr als einen mit xyz=1.

              Ja, das kann ich. Ich habe nämlich nur aufgrund geringerer Schreibarbeit hier eine Date-Spalte aufgeführt. In Wirklichkeit ist es eine Datetimespalte.

              Wahrheitstabellen sind aber was ganz simples. Wenn man prüfen will, ob man einen logischen Ausdruck korrekt optimiert hat, sind sie unverzichtbar. Damit solltest Du Dich befassen.

              Werde ich mir anschauen.

              Jörg

              1. Hallo Jörg,

                Ich weiß wirklich nicht, wo ichs hinpacken soll :-(

                SQL kann überraschend einfach sein, und auch überraschend komplex. Man muss komplexe Queries in ihre Teile gliedern.

                Ist irgendwie, wie beim Ostereier suchen. Da war ich auch nie sonderlich gut drin 😉

                Wenn Du damit meinst, dass Du mit Geduld und strukturiertem Vorgehen ein Problem hast, dann ist deine Zukunft in der Computerwelt sehr steinig.

                AND (((t2.Datum = (SELECT MAX(Datum)
                                                  FROM table2 t2
                                                  WHERE t2.ID = t1.ID) AND XYZ = 1) OR (XYZ = 0)) AND t2.del = 0)
                

                Der Subselect, der das Referenzdatum bestimmt, ist dieser:

                (SELECT MAX(Datum) FROM table2 t2 WHERE t2.ID = t1.ID)

                Dieser Subselect darf nur Sätze mit xyz=1 berücksichtigen.

                Und das tut man wie?

                Zugegeben, nachher sieht die Query merkwürdig aus, sie scheint dann zweimal nacheinander das gleiche abzufragen. Aber ich sagte ja eh schon, dass eine bestimmte Abfrage überflüssig sei 😉

                Rolf

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

                  SQL kann überraschend einfach sein, und auch überraschend komplex. Man muss komplexe Queries in ihre Teile gliedern.

                  Habe ich versucht. Die verflixten Klammern warens, die mich irritiert haben.

                  Ist irgendwie, wie beim Ostereier suchen. Da war ich auch nie sonderlich gut drin 😉

                  Wenn Du damit meinst, dass Du mit Geduld und strukturiertem Vorgehen ein Problem hast, dann ist deine Zukunft in der Computerwelt sehr steinig.

                  Wem sagst Du es? 😂

                  Zugegeben, nachher sieht die Query merkwürdig aus, sie scheint dann zweimal nacheinander das gleiche abzufragen.

                  ... AND XYZ = 1) AND XYZ = 1) OR ... 
                  

                  Finde ich weniger irritierend als die boolsche Optimierung zu "übersehen", ist aber Geaschacksache.

                  Danke für Deine Hilfe 😀

                  Jörg

                  1. Hallo Jörg,

                    Die verflixten Klammern warens, die mich irritiert haben.

                    Ja, ein bisschen Talent für LISP[1] braucht es in jeder Sprache.

                    Rolf

                    --
                    sumpsi - posui - obstruxi

                    1. LISP - Lots of Irritating Superfluous Parentheses 😉 ↩︎

                    1. Hallo Rolf,

                      Genau an diese Übersetzung von LISP ,musste ich auch sofort denken 😉

                      Mein Editor zeigt mir ja normalerweise matching brackets an, leider aber nicht im sql-Teil des php. 😏

                      Jörg