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

Deux SQL LEFT JOINS produisent un résultat incorrect

Les jointures sont traitées de gauche à droite (sauf si les parenthèses dictent le contraire). Si vous LEFT JOIN (ou simplement JOIN , effet similaire) trois courses à un utilisateur vous obtenez 3 rangées (1 x 3 ). Si vous rejoignez ensuite 4 poissonneries pour le même utilisateur, vous en obtenez 12 (3 x 4 ) lignes, multiplication le décompte précédent dans le résultat, et non l'ajout comme vous l'auriez peut-être espéré.
Multipliant ainsi les visites d'épiceries et de poissonneries.

Vous pouvez le faire fonctionner comme ceci :

SELECT u.id
     , u.account_balance
     , g.grocery_visits
     , f.fishmarket_visits
FROM   users u
LEFT   JOIN (
   SELECT user_id, count(*) AS grocery_visits
   FROM   grocery
   GROUP  BY user_id
   ) g ON g.user_id = u.id
LEFT   JOIN (
   SELECT user_id, count(*) AS fishmarket_visits
   FROM   fishmarket
   GROUP  BY user_id
   ) f ON f.user_id = u.id
ORDER  BY u.id;

Pour obtenir des valeurs agrégées pour un ou quelques utilisateurs, sous-requêtes corrélées comme @Vince fourni sont très bien. Pour une table entière ou une grande partie de celle-ci, il est (beaucoup) plus efficace d'agréger les n-tables et de les joindre au résultat une fois . De cette façon, nous n'avons pas non plus besoin d'un autre GROUP BY dans la requête externe.

grocery_visits et fishmarket_visits sont NULL pour les utilisateurs sans aucune entrée associée dans les tables respectives. Si vous avez besoin de 0 à la place (ou n'importe quel nombre arbitraire), utilisez COALESCE dans le SELECT extérieur :

SELECT u.id
     , u.account_balance
     , COALESCE(g.grocery_visits   , 0) AS grocery_visits
     , COALESCE(f.fishmarket_visits, 0) AS fishmarket_visits
FROM   ...