PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Joindre quatre tables impliquant LEFT JOIN sans doublons

Vous avez deux LEFT JOINS :

  • La 1ère jointure gauche peut joindre plusieurs lignes à partir de solved . Dites, 'jane' et 'luke' ont résolu la tâche.
  • La 2e jointure gauche ne peut rejoindre que les utilisateurs nommés "luke" ("luke" dans la condition de jointure !).

Vous obtenez toujours les deux lignes, 'jane' n'est tout simplement pas affichée, la condition de jointure la filtre, mais le LEFT JOIN conserve quand même la ligne dans le résultat et ajoute des valeurs NULL.

Vous pouvez obtenir ce que vous recherchez en utilisant des parenthèses et un [INNER] JOIN au lieu de LEFT JOIN entre solved et users . Le manuel :

Utilisez des parenthèses si nécessaire pour déterminer l'ordre d'imbrication. En l'absence de parenthèses, JOIN s imbriquer de gauche à droite.

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;
  • Utilisation du nom de table users au lieu du mot réservé user .

  • En supposant que users.name est défini unique ou vous pouvez avoir plusieurs utilisateurs nommés 'luke'.

  • Si (task.id, users.id) dans solved est défini UNIQUE ou PRIMARY KEY , vous n'avez pas besoin de DISTINCT du tout.

La requête résultante est non seulement correcte, mais également plus rapide.

Version SQLAlchemy de la requête ci-dessus : (contribué par @van)
Cela suppose que Category , Task et User sont des classes mappées, tandis que solved est une instance de Table (juste une table d'association comme indiqué dans l'exemple de code Many to Many) :

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)
     )