35

我有 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产生正确的结果,即.groceryfishmarket"1", "3", "4"

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

4

3 回答 3

66

连接从左到右处理(除非括号另有说明)。如果您LEFT JOIN(或只是JOIN,类似的效果)向一位用户提供三个杂货,您将获得 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 表并加入一次结果会更有效。这样,我们在外部查询中也不需要另一个。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   ...
于 2012-09-17T17:15:43.273 回答
13

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

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

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
于 2012-09-17T17:24:06.660 回答
3

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

于 2012-09-17T17:18:21.857 回答