从多个表中获取外键的数量

时间:2014-07-14 20:12:00

标签: sql postgresql left-join aggregate-functions correlated-subquery

我有3张桌子,表B& C通过外键引用表A.我想在PostgreSQL中编写一个查询来获取A中的所有id以及它们从B&中出现的总数。 C.

   a      |     b      |     c
-----------------------------------    
id | txt  |  id | a_id |  id | a_id  
---+----  |  ---+----- |  ---+------ 
1  |  a   |  1  |  1   |  1  |  3    
2  |  b   |  2  |  1   |  2  |  4    
3  |  c   |  3  |  3   |  3  |  4    
4  |  d   |  4  |  4   |  4  |  4    

所需输出(仅来自A& B& C中的总数):

id | Count
---+-------  
1  |  2      -- twice in B
2  |  0      -- occurs nowhere
3  |  2      -- once in B & once in C
4  |  4      -- once in B & thrice in C
到目前为止

SQL SQL Fiddle

SELECT a_id, COUNT(a_id)
FROM
( SELECT a_id FROM b
  UNION ALL 
  SELECT a_id FROM c
) AS union_table
GROUP BY a_id

我写的查询来自B& C并计算出现次数。但是如果密钥没有出现在B或C中,它就不会出现在输出中(例如输出中的id = 2)。如何从表A&表中开始我的选择?加入/联盟B& C获得所需的输出

3 个答案:

答案 0 :(得分:4)

如果查询涉及b和/或c的大部分,则首先聚合并稍后加入会更有效。
我希望这两个变体要快得多:

SELECT a.id,
      ,COALESCE(b.ct, 0) + COALESCE(c.ct, 0) AS bc_ct
FROM   a
LEFT   JOIN (SELECT a_id, count(*) AS ct FROM b GROUP BY 1) b USING (a_id)
LEFT   JOIN (SELECT a_id, count(*) AS ct FROM c GROUP BY 1) c USING (a_id);

您需要考虑a_id和/或a中某些b根本不存在的可能性。 count()永远不会返回NULL,但是LEFT JOIN面对的是一种冷漠的感觉,但仍会留下NULL个缺失行的值。您必须NULL做准备。使用 COALESCE()

或两个表中的UNION ALL a_id,聚合,然后加入:

SELECT a.id
      ,COALESCE(ct.bc_ct, 0) AS bc_ct
FROM   a
LEFT   JOIN (
   SELECT a_id, count(*) AS bc_ct
   FROM (
      SELECT a_id FROM b
      UNION ALL
      SELECT a_id FROM c
      ) bc
   GROUP  BY 1
   ) ct USING (a_id);

可能慢一些。但仍然比目前提出的解决方案更快。你可以没有COALESCE()但仍然没有任何行。在这种情况下,您可能会偶尔获得NULL的{​​{1}}值。

答案 1 :(得分:3)

另一种选择:

SELECT
    a.id,
    (SELECT COUNT(*) FROM b WHERE b.a_id = a.id) +
    (SELECT COUNT(*) FROM c WHERE c.a_id = a.id)
FROM
    a

答案 2 :(得分:2)

将左连接与子查询一起使用:

SELECT a.id, COUNT(x.id)
FROM a
LEFT JOIN (
    SELECT id, a_id FROM b
    UNION ALL
    SELECT id, a_id FROM c
) x ON (a.id = x.a_id)
GROUP BY a.id;