sql >> Base de Datos >  >> RDS >> PostgreSQL

Une cuatro tablas que involucran LEFT JOIN sin duplicados

Tienes dos LEFT JOINS :

  • La primera unión a la izquierda puede unirse a varias filas desde solved . Diga, 'jane' y 'luke' resolvieron la tarea.
  • La segunda unión a la izquierda solo puede unirse a usuarios llamados 'luke' (¡'luke' en la condición de unión!).

Todavía obtienes ambos filas, 'jane' simplemente no se muestra, la condición de unión la filtra, pero LEFT JOIN conserva la fila en el resultado de todos modos y agrega valores NULL.

Puede lograr lo que busca usando paréntesis y un [INNER] JOIN en lugar de LEFT JOIN entre solved y users . El manual:

Use paréntesis si es necesario para determinar el orden de anidamiento. En ausencia de paréntesis, JOIN s anidan de izquierda a derecha.

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;
  • Usando el nombre de la tabla users en lugar de la palabra reservada user .

  • Suponiendo que users.name se define único o puede tener múltiples usuarios llamados 'luke'.

  • Si (task.id, users.id) en solved se define UNIQUE o PRIMARY KEY , no necesitas DISTINCT en absoluto.

La consulta resultante no solo es correcta, sino también más rápida.

Versión SqlAlchemy de la consulta anterior: (aportado por @van)
Esto supone que Category , Task y User son clases mapeadas, mientras que solved es una instancia de Table (solo una tabla de asociación como se muestra en el ejemplo de código 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)
     )