了解多表连接与聚合

use*_*760 5 postgresql join aggregate

我有一个关于如何JOIN在多个表上工作的基本问题。我想计算link1&中外键的出现次数link2

CREATE TABLE main (
   id SERIAL PRIMARY KEY,
   name text NOT NULL
);

CREATE TABLE link1 (
   id SERIAL PRIMARY KEY,
   main_id integer NOT NULL,
   CONSTRAINT main_id_fk FOREIGN KEY (main_id) REFERENCES main (id)
);

-- link2 is similar to link1
Run Code Online (Sandbox Code Playgroud)

SQL小提琴

当两列中的计数都不为零时,为什么下面的查询会给出计数的乘积(而不是总和)。

SELECT main.id, COUNT(link1.main_id) + COUNT(link2.main_id)
FROM main
LEFT JOIN link1 ON main.id=link1.main_id
LEFT JOIN link2 ON main.id=link2.main_id
GROUP BY main.id
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 7

您看到的是“代理交叉连接”。先聚合,然后加入:

SELECT m.id, COALESCE(l1.ct, 0) + COALESCE(l2.ct, 0) AS total_ct
FROM   main m
LEFT   JOIN (
   SELECT main_id, count(*) AS ct
   FROM   link1
   GROUP  BY main_id
   ) l1 ON l1.main_id = m.id
LEFT   JOIN (
   SELECT main_id, count(*) AS ct
   FROM   link2
   GROUP  BY main_id
   ) l2 ON l2.main_id = m.id
ORDER BY m.id;
Run Code Online (Sandbox Code Playgroud)

SQL小提琴。

难道不是与多个不合格的连接和乘行count(DISTINCT ...),以后再修改错误。它碰巧在这种情况下工作,因为计算 DISTINCT link1.id/link2.id与所需的结果一致,但它不必要地昂贵且容易出错。

这些相关答案中的详细解释和几个语法变体:

  • 这具有更好的性能并且适用于我的用例。这应该是公认的答案 (3认同)

use*_*760 3

我会尝试自己回答这个问题。考虑&LEFT JOIN之间。输出将是mainlink1

main.id   link1.main_id
   1          1
   1          1
   2          2
   3         NULL  
   4         NULL  
Run Code Online (Sandbox Code Playgroud)

现在用 执行LEFT JOIN上面的表格link2,输出将是:

main.id    link1.main_id    link2.main_id
   1             1               NULL  
   1             1               NULL  
   2             2                2
   2             2                2     -- Error : double counting for link1
   3            NULL              3
   4            NULL                   
Run Code Online (Sandbox Code Playgroud)

现在计算 的出现次数main_id并对它们求和(按 分组main.id

main.id        Count
   1             2
   2             2 + 2  
   3             1
   4             0
Run Code Online (Sandbox Code Playgroud)

因此,两个连续的事件LEFT JOIN是顺序发生的,而不是并行发生的。获取计数的正确方法是分别进行 2 个查询,然后将结果相加

根据@a1ex07更新另一种方法是

SELECT main.id, COUNT(DISTINCT link1.id) + COUNT(DISTINCT link2.id)
FROM main
LEFT JOIN link1 ON main.id=link1.main_id
LEFT JOIN link2 ON main.id=link2.main_id
GROUP BY main.id
Run Code Online (Sandbox Code Playgroud)

  • @a1ex07:虽然“DISTINCT”在这里起到了作用,但这有点像给猪涂口红...... (2认同)