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)
danssolved
est définiUNIQUE
ouPRIMARY KEY
, vous n'avez pas besoin deDISTINCT
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)
)