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,
JOINs 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
usersau lieu du mot réservé.user -
En supposant que
users.nameest défini unique ou vous pouvez avoir plusieurs utilisateurs nommés 'luke'. -
Si
(task.id, users.id)danssolvedest définiUNIQUEouPRIMARY KEY, vous n'avez pas besoin deDISTINCTdu 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)
)