Two SQL LEFT JOINS produce incorrect result

后端 未结 3 1758
青春惊慌失措
青春惊慌失措 2020-11-22 08:56

I have 3 tables:

users(id, account_balance)
grocery(user_id, date, amount_paid)
fishmarket(user_id, date, amount_paid)

Both fishmarke

3条回答
  •  忘了有多久
    2020-11-22 09:35

    Joins are processed left to right (unless parentheses dictate otherwise). If you LEFT JOIN (or just JOIN, similar effect) three groceries to one user you get 3 rows (1 x 3). If you then join 4 fishmarkets for the same user, you get 12 (3 x 4) rows, multiplying the previous count in the result, not adding to it, like you may have hoped for.
    Thereby multiplying the visits for groceries and fishmarkets alike.

    You can make it work like this:

    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;
    

    To get aggregated values for one or few users, correlated subqueries like @Vince provided are just fine. For a whole table or major parts of it, it is (much) more efficient to aggregate the n-tables and join to the result once. This way, we also do not need another GROUP BY in the outer query.

    grocery_visits and fishmarket_visits are NULL for users without any related entries in the respective tables. If you need 0 instead (or any arbitrary number), use COALESCE:

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

提交回复
热议问题