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

cli*_*ick 5 sql postgresql aggregate-functions left-join 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    
Run Code Online (Sandbox Code Playgroud)

所需输出(只是A中的id和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
Run Code Online (Sandbox Code Playgroud)

SQL到目前为止SQL小提琴:

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
Run Code Online (Sandbox Code Playgroud)

我写的查询从B&C获取并计算出现次数.但是如果密钥没有出现在B或C中,它就不会出现在输出中(例如输出中的id = 2).如何从表A和join/union B&C开始我的选择以获得所需的输出

Erw*_*ter 5

如果查询涉及大部分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);
Run Code Online (Sandbox Code Playgroud)

您需要考虑a_ida和/或中根本不存在某些内容的可能性bcount()永远不会返回NULL,但是面对LEFT JOIN,这是冷酷的安慰,NULL尽管如此,这仍然会给您留下缺失行的值。你必须NULL. 使用COALESCE().

或者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);
Run Code Online (Sandbox Code Playgroud)

可能比较慢。但仍然比目前提出的解决方案更快。而且你可以不做COALESCE()并且仍然不会丢失任何行。在这种情况下,您可能会偶尔获得 的NULLbc_ct