当表不直接相关时,选择多个计数

时间:2013-02-27 16:58:14

标签: sql postgresql

用户表:

  • user_id(每个用户不同)
  • source_id(用户可能拥有相同的来源)

规则表:

  • white_rules
  • black_rules
  • general_rules

这些表看起来都一样,并且有:

  • victim_id(与用户表中的user_id共同关联)。
  • rule_id(与此处不重要的不同表格共同关联)

我需要的是每个source_id提取每种类型的总规则数量(白色,黑色,一般)。

示例:

  • source_id:5 --->共有70条白色规则,共32条黑色规则,共21条一般规则
  • source_id:7 --->总共2条白色规则,共0条黑色规则,共4条一般规则

等等...对于用户表中列出的所有不同来源。

我试过的是:

SELECT source_id,
count(w.victim_id) as total_white,
count(b.victim_id) as total_black,
count(g.victim_id) as total_general
from users
LEFT JOIN white_rules as w ON (user_id=w.victim_id)
LEFT JOIN black_rules as b ON (user_id=b.victim_id)
LEFT JOIN general_rules as g ON (user_id=g.victim_id)
where deleted='f' and source is not null
group by source;

但是我获得的结果表的数字错误(更高)超出了我的预期, 所以我一定做错了:) 我会欣赏正确方向的任何铰链。

1 个答案:

答案 0 :(得分:0)

您需要在子查询中进行计数,或计数不同,因为您的多个1对多关系导致交叉连接。我不知道你的数据,但想象一下这个场景:

用户:

User_ID |   Source_ID
--------+--------------
  1     |      1  

<强> White_Rules

Victim_ID | Rule_ID
----------+-------------
   1      |    1
   1      |    2

<强> Black_Rules

Victim_ID | Rule_ID
----------+-------------
   1      |    3
   1      |    4

如果你跑

SELECT  Users.User_ID, 
        Users.Source_ID, 
        White_Rules.Rule_ID AS WhiteRuleID, 
        Black_Rules.Rule_ID AS BlackRuleID
FROM    Users
        LEFT JOIN White_Rules
            ON White_Rules.Victim_ID = Users.User_ID
        LEFT JOIN Black_Rules
            ON Black_Rules.Victim_ID = Users.User_ID

您将获得White_Rules.Rule_IDBlack_Rules.Rule_ID的所有组合:

User_ID | Source_ID | WhiteRuleID | BlackRuleID
--------+-----------+-------------+-------------
  1     |    1      |      1      |      3
  1     |    1      |      2      |      4
  1     |    1      |      1      |      3
  1     |    1      |      2      |      4

因此计算结果将返回4条白色规则和4条黑色规则,即使每条规则只有2条。

如果您将查询更改为此内容,则应获得所需的结果:

SELECT  Users.Source_ID,
        SUM(COALESCE(w.TotalWhite, 0)) AS TotalWhite,
        SUM(COALESCE(b.TotalBlack, 0)) AS TotalBlack,
        SUM(COALESCE(g.TotalGeneral, 0)) AS TotalGeneral
FROM    Users
        LEFT JOIN
        (   SELECT  Victim_ID, COUNT(*) AS TotalWhite
            FROM    White_Rules
            GROUP BY Victim_ID
        ) w
            ON w.Victim_ID = Users.User_ID
        LEFT JOIN
        (   SELECT  Victim_ID, COUNT(*) AS TotalBlack
            FROM    Black_Rules
            GROUP BY Victim_ID
        ) b
            ON b.Victim_ID = Users.User_ID
        LEFT JOIN
        (   SELECT  Victim_ID, COUNT(*) AS TotalGeneral
            FROM    General_Rules
            GROUP BY Victim_ID
        ) g
            ON g.Victim_ID = Users.User_ID
WHERE   Deleted = 'f'
AND     Source IS NOT NULL
GROUP BY Users.Source_ID

<强> Example on SQL Fiddle

另一种选择是:

SELECT  Users.Source_ID,
        COUNT(Rules.TotalWhite) AS TotalWhite,
        COUNT(Rules.TotalBlack) AS TotalBlack,
        COUNT(Rules.TotalGeneral) AS TotalGeneral
FROM    Users
        LEFT JOIN
        (   SELECT  Victim_ID, 1 AS TotalWhite, NULL AS TotalBlack, NULL AS TotalGeneral
            FROM    White_Rules
            UNION ALL
            SELECT  Victim_ID, NULL AS TotalWhite, 1 AS TotalBlack, NULL AS TotalGeneral
            FROM    Black_Rules
            UNION ALL
            SELECT  Victim_ID, NULL AS TotalWhite, NULL AS TotalBlack, 1 AS TotalGeneral
            FROM    General_Rules
        ) Rules
            ON Rules.Victim_ID = Users.User_ID
WHERE   Deleted = 'f'
AND     Source IS NOT NULL
GROUP BY Users.Source_ID

<强> Example on SQL Fiddle