PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Verbinden Sie vier Tabellen mit LEFT JOIN ohne Duplikate

Sie haben zwei LEFT JOINS :

  • Der 1. linke Join kann mit mehreren Zeilen von solved verknüpft werden . Sprich, 'jane' und 'luke' haben die Aufgabe gelöst.
  • Der 2. linke Beitritt kann nur mit Benutzern mit dem Namen „luke“ („luke“ in der Join-Bedingung!) verknüpft werden.

Sie erhalten immer noch beides Zeilen, 'jane' wird einfach nicht angezeigt, die Join-Bedingung filtert sie heraus, aber der LEFT JOIN behält die Zeile im Ergebnis trotzdem bei und hängt NULL-Werte an.

Sie können erreichen, wonach Sie suchen, indem Sie Klammern verwenden und einen [INNER] JOIN anstelle des LEFT JOIN zwischen solved und users . Das Handbuch:

Verwenden Sie ggf. Klammern, um die Verschachtelungsreihenfolge festzulegen. Wenn keine Klammern vorhanden sind, JOIN s Nest von links nach rechts.

SELECT c.name AS cat_name, t.name AS task_name, u.name AS user_name
FROM   task t
JOIN   category c ON cat.id = t.category_id
LEFT   JOIN
      (solved s JOIN users u ON u.id = s.user_id AND u.name = 'luke') ON s.task_id = t.id
ORDER  BY 1, 2, 3;
  • Verwenden des Tabellennamens users anstelle des reservierten Wortes user .

  • Angenommen, dass users.name ist eindeutig definiert oder Sie können mehrere Benutzer mit dem Namen „luke“ haben.

  • Wenn (task.id, users.id) in solved ist UNIQUE definiert oder PRIMARY KEY , brauchen Sie DISTINCT nicht überhaupt.

Die resultierende Abfrage ist nicht nur korrekt, sondern auch schneller.

SqlAlchemy-Version der obigen Abfrage: (Beigetragen von @van)
Dies setzt voraus, dass Category , Task und users sind zugeordnete Klassen, während sie solved werden ist eine Instanz von Table (nur eine Assoziationstabelle, wie im Codebeispiel Many to Many gezeigt):

user_name = 'luke'
q = (session.query(Category.name, Task.name, User.name)
     .select_from(Task)
     .join(Category)
     .outerjoin(
         join(solved, User,
              (solved.c.user_id == User.id) & (User.name == user_name),
         ))
     .order_by(Category.name, Task.name, User.name)
     )