Zwei einzelne Aufrufe zusammenfassen

Moin zusammen,

ich versuche gerade unsere Anwendung etwas performanter zu machen.
Dazu wollte ich zwei SQL-Aufrufe zu einem zusammenfassen.
Meine erste SQL sieht in etwa folgendermaßen aus:

 SELECT
   guid,
   SomeValue,
   timestamp_edit,
   SuperID,
   datensatz_status
 FROM
   Tabelle_1
 WHERE
   ((datensatz_status = 0) OR (datensatz_status = 1))
 ORDER BY
   timestamp_save, guid
 LIMIT 150 OFFSET 0

Nun durchlaufe ich in meinem Programm die Datensätze, schreibe die Werte jeweils weg und führe dann aber für alle Datensätze folgende Query aus:

SELECT
  SomeValue2
FROM
  Tabelle_2
WHERE
  (SuperID2 = :TheID)
ORDER BY
  timestamp_save DESC
LIMIT 1

Mein Problem ist, dass ich beim Zusammenführen keine Idee habe, wie ich ein vernünftiges Join machen kann, welches das Limit beachtet.
Ich will pro Datensatz der ersten Tabelle ja nur den neusten der zweiten Tabelle haben.
Nach langem rumprobieren bin ich hier gelandet:

SELECT
  a.*, b.SomeValue2
FROM
(
 SELECT
   guid,
   timestamp_edit,
   timestamp_save,
   SomeValue,
   SuperID,
   datensatz_status
 FROM
   Tabelle_1
 WHERE
   ((datensatz_status = 0) OR (datensatz_status = 1))
 LIMIT 150 OFFSET 0
) a
LEFT JOIN
(
  SELECT
    SomeValue2,
    timestamp_save,
    SuperID2
  FROM
    Tabelle_2
  WHERE
    timestamp_save in (SELECT MAX(timestamp_save) as Val FROM Tabelle_2 GROUP BY SuperID2)
) b
  ON a.SuperID = b.SuperID2
 ORDER BY
   a.timestamp_save, a.guid

Damit bekomme ich in meinen Testdaten zwar genau das richtige Ergebnis, aber die
timestamp_edit und timestamp_save-Felder sind nicht unique, so dass das neuste timestamp_save von SuperID2 = ‘abc’ auch gleichzeitig das falsche timestamp_save von SuperID2 = ‘123’ sein kann. Zum Einen, weil ich in Funktionen oft hingehe und nur einmal den Zeitstempel ermittle und diesen dann an mehreren Stellen wieder verwende, zum Anderen, weil mehrere Client-PCs in die Tabelle schreiben können.

Anmerkung: Ich habe die SQLs für leichteres Verständnis gekürzt und Feld sowie Tabellennamen angepasst, weil ich nicht weiß, wie das Datenschutztechnisch ein Problem sein könnte.
Ich hoffe, dass mir hierbei keine Tippfehler passiert sind.

Anmerkung 2: Das ganze sollte auch funktionieren, wenn es keinen passenden Datensatz in der zweiten Tabelle gibt (Feld = null).

Hat jemand die “richtige” Lösung für dieses Problem? Meine ist ja nicht “richtig”.

Vielleicht kann ich das mit einer allgemeinen Antwort selbst lösen, wenn jemand die Antwort auf die Frage hat “Wie kann ich zwei SQLs zusammenführen bei der die erste SQL Datensätze ermittelt und die zweite SQL den jeweils aktuellsten Datensatz aus einer zweiten Tabelle dazu laden muss?” (Wenn die Frage so korrekt gestellt ist.) Aber ich habe zumindest nichts zu dieser Fragestellung finden können.

Liebe Grüße aus dem Norden
Incocnito

wenn ich Dich richtig verstehe brauchst Du nur einmalig ein select auf tabelle_2 machen und das per der id joinen.

Also, ich vermute du hast:

postgres=# create table tabelle_2 (id int primary key, somevalue2 int, ts_save timestamp);
CREATE TABLE
postgres=# select distinct on (id, somevalue2) id, somevalue2, ts_save from tabelle_2 order by id, somevalue2, ts_save desc;
 id | somevalue2 | ts_save 
----+------------+---------
(0 rows)
1 „Gefällt mir“

Moin AKretschmer,

so in etwa?

SELECT
  a.*, b.SomeValue2
FROM
(
 SELECT
   guid,
   timestamp_edit,
   timestamp_save,
   SomeValue,
   SuperID,
   datensatz_status
 FROM
   Tabelle_1
 WHERE
   ((datensatz_status = 0) OR (datensatz_status = 1))
 LIMIT 150 OFFSET 0
) a
LEFT JOIN
(
  SELECT DISTINCT
  ON (
	  SuperID2,
	  timestamp_save
  )
    SomeValue2,
    timestamp_save,
    SuperID2
  FROM
    Tabelle_2
 ORDER BY
    SuperID2, timestamp_save
) b
  ON a.SuperID = b.SuperID2
 ORDER BY
   a.timestamp_save, a.guid

Ich bin leider nicht so firm in SQL. Das lässt sich zumindest ausführen.
Vielen Dank für die Mühe bis hierhin schonmal.

Liebe Grüße
Incocnito

ja, prüfe es doch mal, ob es korrekte / gewünschte Resultate liefert. Das ist für Dich einfacher als für mich …

Yay,
das sieht gut aus.
Ich habe in meiner Testdaten-Datenbank mal alle Zeitfelder gleich gesetzt.
Bei meiner alten SQL kam (wie erwartet) ein Datensatz zu viel raus,
aber mit deiner Anpassung kommen nur (korrekt) zwei Datensätze bei rum.

Ich verstehe zwar nicht wirklich, wie das SELECT DISTINCT ON an der Stelle funktioniert,
aber es funktioniert. Vielen Dank dafür.

Liebe Grüße aus dem Norden
Incocnito

Gut. DISTINCT ON (…) ist hier beschrieben. Beachte: das ist eine Erweiterung des SQL-Standards, wird also so erst einmal nur in PostgreSQL funktionieren. Ist aber cool …

Ich nehme an, tabelle_1.guid ist der Primärschlüssel.

Mit DISTINCT ON (performant, nicht standardkonform):

SELECT DISTINCT ON (t1.timestamp_save, t1.guid)
       t1.guid,
       t1.somevalue,
       t1.timestamp_edit,
       t1.superid,
       t1.datensatz_status,
       t2.somevalue2
FROM tabelle_1 AS t1
   LEFT JOIN tabelle_2 AS t2
      ON t1.superid = t2.superid2
WHERE t1.datensatz_status IN (0, 1)
ORDER BY t1.timestamp_save, t1.guid, t2.timestamp_save DESC
LIMIT 150 OFFSET 0;
1 „Gefällt mir“

Ja, richtig “guid” ist jeweils der Primärschlüssel.
Ich probiere das morgen auch nochmal aus.
(Außer das mit WHERE-IN, weil das in der tatsächlichen Anwendung sich etwas anders darstellt :wink: )
Danke für die Mühe schonmal.

Liebe Grüße aus dem Norden
Incocnito

Leider war es doch falsch. Wenn es den Datensatz mit der gleichen ID gibt, aber unterschiedlicher Zeit, dann zeigt er den doppelt an. Ich kann zwar das “Distinct on” auf die ID kürzen, dann passt das wieder, aber ich sage ihm ja nirgends, dass ich jeweils den aktuellsten Datensatz behalten will.

Edit: Wait! Ich habe das Order By beim Testen vergessen. Das scheint er bereits auf das Zwischenergebnis anzuwenden und somit doch den richtigen Datensatz auszugeben.