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
usersen lugar de la palabra reservada.user -
Suponiendo que
users.namese define único o puede tener múltiples usuarios llamados 'luke'. -
Si
(task.id, users.id)ensolvedse defineUNIQUEoPRIMARY KEY, no necesitasDISTINCTen 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)
)