mysql, Query, Immer nur der letzte Eintrag
Jörg
- mysql
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
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
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
Hallo Jörg,
naja, dann musst Du
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
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 zuB 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
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
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
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
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
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