Ravaelle: Probleme mit left join, bekomme zu viele Ergebnisse

Hallo, in der einen Tabelle (1) habe ich die Städte, in der anderen Tabelle (2) sind Daten zu den Städten (auch mehrere pro Stadt) Jetzt rufe ich die erste Tabelle auf mit allen Stadtnamen und lese mittels LEFT JOIN die letzten Einträge zu den Städten ein.

SELECT a.url, b.neu_time FROM city a
 LEFT JOIN datensatz b ON a.stadt = b.stadt ORDER BY b.neu_time DESC

ABER… es werden alle Datensätze aus der Tabelle 2 ausgeben, es sollten aber nur die Datensätze aus der Tabelle 1 zuzüglich der 2 ausgegeben werden. (und nicht anders rum)

Ravaelle

  1. Tach!

    in der einen Tabelle (1) habe ich die Städte, in der anderen Tabelle (2) sind Daten zu den Städten (auch mehrere pro Stadt) Jetzt rufe ich die erste Tabelle auf mit allen Stadtnamen und lese mittels LEFT JOIN die letzten Einträge zu den Städten ein.

    SELECT a.url, b.neu_time FROM city a
     LEFT JOIN datensatz b ON a.stadt = b.stadt ORDER BY b.neu_time DESC
    

    Es gibt eine Reihenfolge, in der die einzelnen Klauseln eines SQL-Statements abgearbeitet werden. Das Join findet vor dem Order By statt. Das heißt, es entsteht zuerst ein kartesiches Produkt aus den beiden Tabellen und das wird dann nur noch sortiert.

    ABER… es werden alle Datensätze aus der Tabelle 2 ausgeben, es sollten aber nur die Datensätze aus der Tabelle 1 zuzüglich der 2 ausgegeben werden. (und nicht anders rum)

    In dem Fall musst du die Daten der zweiten Tabelle soweit einschränken, wie du sie haben möchtest, bevor das Join wirkt.

    Formuliere zunächst eine Query auf die zweite Tabelle, die nur einen Datensatz pro Stadt ergibt. Dazu wirst du sicher über die Stadt gruppieren müssen und dabei eine Aggregatfunktion nehmen, die dir "letzter Eintrag" ermittelt, also vermutlich MAX(neu_time).

    Wenn diese Query soweit steht, kannst du sie als Subselect im Join verwenden.

    dedlfix.

    1. Hallo dedlfix,

      es entsteht kein kartesisches Produkt. So wie ich den Begriff verstehe, steht er für alle möglichen Paarungen aus 2 Tabellen (oder n-Tupel aus N Tabellen). D.h. bei 10 city-Rows und 12 datensatz-Rows würde das kartesische Produkt 120 Rows umfassen. Hier wird aber eine ON Klausel gesetzt und das reduziert das kartesische Produkt auf die möglichen Paarungen mit gleicher Stadt. Bei einem LEFT JOIN also im worst case - äh, knobel - 21 Rows.

      In dem Fall musst du die Daten der zweiten Tabelle soweit einschränken, wie du sie haben möchtest, bevor das Join wirkt.

      Wie gesagt: ON Klausel. Die Einschränkung ist da. Weitere Einschränkungen kann man natürlich machen, entweder im ON, um den JOIN selbst zu beschränken, oder nachher im WHERE, um das JOIN-Ergebnis zu beschränken. Ob der SQL Server eine WHERE Einschränkung vor, während oder nach dem JOIN anwendet, ist aber Sache des Query Optimizers und sollte dem SQL-Verfasser egal sein. Sollte... Es gibt natürlich Queries, die der Optimizer nicht in den Griff bekommt und die man durch Umformulieren performanter machen kann. Aber das hängt dann auch am DBMS, wie gut der Optimizer ist.

      Ob man einen JOIN oder Subselect verwendet, sollte (sofern ein Subselect die Anforderungen erfüllen kann) der Performance oder dem Abfrageergebnis egal sein. Es gibt Argumente pro und contra Join, was die Lesbarkeit oder Verständlichkeit angeht, die haben wir oft genug gewechselt…

      Meine eigene Mutmaßung zu dem, was passiert, habe ich in einem anderen Posting aufgeschrieben.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Tach!

        es entsteht kein kartesisches Produkt. So wie ich den Begriff verstehe, steht er für alle möglichen Paarungen aus 2 Tabellen (oder n-Tupel aus N Tabellen).

        Es ist eine Kombination aus kartesischem Produkt und Selektion. Aus dem k.P. werden diejenigen Datensätze selektiert, die zur ON-Klausel passen, also bei denen die Stadtnamen übereinstimmen.

        In dem Fall musst du die Daten der zweiten Tabelle soweit einschränken, wie du sie haben möchtest, bevor das Join wirkt.

        Wie gesagt: ON Klausel. Die Einschränkung ist da. Weitere Einschränkungen kann man natürlich machen, entweder im ON, um den JOIN selbst zu beschränken, oder nachher im WHERE, um das JOIN-Ergebnis zu beschränken.

        Das geht nur in dem Fall nicht, so wie ich ihn verstanden habe. Das WHERE selektiert anhand der Daten innerhalb einer Ergebniszeile. Gesucht ist aber der "letzte Einträge zu den Städten". Dazu müssen die Zeilen untereinander verglichen werden. Das geht aber nur indirekt über eine Gruppierung mit Aggregatfunktion.

        Ob man einen JOIN oder Subselect verwendet,

        Das war eigentlich gar nicht mein Thema. Aber ja, wenn aus der zweiten Tabelle lediglich ein einzelnes Feld interessiert, dann könnte man auch Tabelle 1 allein und ungejoint befragen und ein Correlated Subselect in der SELECT-Klausel für das Feld aus Tabelle 2 hinzufügen. Da ich aber den Eindruck hatte, dass mehr als ein Feld interessiert, hab ich an diese Variante gar nicht gedacht.

        Mein Vorschlag war diesmal, die Ergebnismenge der Tabelle 2 auf den jeweils letzten Datensatz zu beschränken, und diese Zwischenmenge mit der ersten Tabelle zu joinen. Also das Subselect der Tabelle 2 im Join.

        dedlfix.

        1. Hallo dedlfix,

          Gesucht ist aber der "letzte Einträge zu den Städten".

          Das hatte ich überlesen. Ravaelle schrieb: ich lese die letzten Einträge zu den Städten ein, de facto ermittelt das SQL aber alle Einträge je Stadt.

          Ravaelle müsste also spezifizieren, was „die letzten“ Einträge sein sollen. Alle nach einem bestimmten Datum (geht mit WHERE) oder die x neuesten (das ist nicht trivial bzw braucht neues SQL mit Windowing-Funktionen).

          Wie immer: in der richtig gestellten Frage liegt schon die halbe Antwort.

          Rolf

          --
          sumpsi - posui - obstruxi
  2. Hallo Ravaelle,

    ABER… es werden alle Datensätze aus der Tabelle 2 ausgeben

    Die Query sollte zu jeder Stadt in der city Tabelle alle datensatz-Rows mit gleicher Stadt finden. Gibt es in Tabelle 2 keine Einträge zur Stadt, bewirkt der LEFT JOIN, dass die verwendeten Spalten aus Tabelle 2 den Wert NULL haben.

    Wir kennen deine Daten nicht. Gibt es in Tabelle 2 denn Rows mit einer Stadt, die in der city-Tabelle nicht vorkommt? Wenn nämlich in Tabelle 2 nur Städte vorkommen, die auch in der city-Tabelle stehen, ist es völlig logisch, dass im Ergebnis alle Sätze aus Tabelle 2 ausgegeben werden.

    Vielleicht solltest Du den Stadtnamen einfach mal mit ausgeben. Wenn es eine ID Spalte in city und datensatz Table gibt, gib sie auch mit aus. Dann sollte es klarer werden, was passiert.

    Rolf

    --
    sumpsi - posui - obstruxi