两个 SQL LEFT JOINS 产生不正确的结果

问题描述 投票:0回答:4

我有3张桌子:

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

fishmarket
grocery
表可能会多次出现同一 user_id,且具有不同的日期和支付金额,或者对于任何给定用户根本没有任何内容。 当我尝试以下查询时:

SELECT
     t1."id" AS "User ID",
     t1.account_balance AS "Account Balance",
     count(t2.user_id) AS "# of grocery visits",
     count(t3.user_id) AS "# of fishmarket visits"
FROM users t1
LEFT OUTER JOIN grocery t2 ON (t2.user_id=t1."id") 
LEFT OUTER JOIN fishmarket t3 ON (t3.user_id=t1."id") 
GROUP BY t1.account_balance,t1.id
ORDER BY t1.id

它会产生不正确的结果:

"1", "12", "12"

但是,当我尝试仅对一张表进行
LEFT JOIN
时,它会为
grocery
fishmarket
访问产生正确的结果,即
"1", "3", "4"

我在这里做错了什么?
我正在使用 PostgreSQL 9.1。

sql postgresql left-join aggregate-functions
4个回答
72
投票

连接从左到右处理(除非括号另有规定)。如果您向一名用户

LEFT JOIN
(或只是
JOIN
,类似的效果)购买 3 种杂货,您将获得 3 行 (1 x 3)。如果您随后为同一用户加入 4 个鱼市,您将获得 12 (3 x 4) 行,乘以结果中的先前计数,而不是像您希望的那样添加
从而增加杂货店和鱼市场的访问量。

你可以让它像这样工作:

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;

要获取一个或几个用户的聚合值,相关子查询像@Vince提供的就可以了。对于整个表或其中的主要部分,聚合 n 个表并连接到结果once(更)有效。这样,我们在外部查询中也不需要另一个

GROUP BY

grocery_visits
fishmarket_visits
NULL
,适用于相应表中没有任何相关条目的用户。如果您需要
0
(或任何任意数字),请在外部 COALESCE 中使用
SELECT

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

15
投票

对于您的原始查询,如果您取消分组依据以查看预先分组的结果,您将了解为什么创建您收到的计数。

也许以下使用子查询的查询可以达到您的预期结果:

SELECT
 t1."id" AS "User ID",
 t1.account_balance AS "Account Balance",
 (SELECT count(*) FROM grocery     t2 ON (t2.user_id=t1."id")) AS "# of grocery visits",
 (SELECT count(*) FROM fishmarket  t3 ON (t3.user_id=t1."id")) AS "# of fishmarket visits"
FROM users t1
ORDER BY t1.id

4
投票

因为用户表连接到杂货表时,匹配到了3条记录。然后这 3 条记录中的每一条都与 Fishmarket 中的 4 条记录相匹配,产生 12 条记录。您需要子查询来获取您要查找的内容。


0
投票

SELECT t1."id" AS "User ID", t1.account_balance AS "Account Balance", Sum(Case When t2.user_id is null then 0 else 1 end) AS "# of grocery visits", Sum(Case When t3.user_id is null then 0 else 1 end) AS "# of fishmarket visits" FROM users t1 LEFT OUTER JOIN grocery t2 ON (t2.user_id=t1."id")  LEFT OUTER JOIN fishmarket t3 ON (t3.user_id=t1."id")  GROUP BY t1.account_balance,t1.id ORDER BY t1.id

上述内容还允许您根据需要添加其他标准

© www.soinside.com 2019 - 2024. All rights reserved.