2

我有三张桌子:

CREATE TABLE foo (
    id bigint PRIMARY KEY,
    name text NOT NULL
);

CREATE TABLE foo_bar (
    id bigint PRIMARY KEY,
    foo_id bigint NOT NULL
);

CREATE TABLE tag (
    name text NOT NULL,
    target_id bigint NOT NULL,
    PRIMARY KEY (name, target_id)
);

我正在尝试创建一个视图,以便获取 table 的所有字段、 wherefoo中的项目数以及foo_barwherefoo.id = foo_bar.foo_id中所有标签的文本数组foo.id = tag.target_id。如果我们有:

INSERT INTO foo VALUES (1, 'one');
INSERT INTO foo VALUES (2, 'two');
INSERT INTO foo_bar VALUES (1, 1);
INSERT INTO foo_bar VALUES (2, 1);
INSERT INTO foo_bar VALUES (3, 2);
INSERT INTO foo_bar VALUES (4, 1);
INSERT INTO foo_bar VALUES (5, 2);
INSERT INTO tag VALUES ('a', 1);
INSERT INTO tag VALUES ('b', 1);
INSERT INTO tag VALUES ('c', 2);

结果应返回:

foo.id    | foo.name     | count       | array_agg
--------------------------------------------------
1         | one          | 3           | {a, b}
2         | two          | 2           | {c}

这是我到目前为止所拥有的:

SELECT DISTINCT f.id, f.name, COUNT(b.id), array_agg(t.name)
FROM foo AS f, foo_bar AS b, tag AS t
WHERE f.id = t.target_id AND f.id = b.foo_id
GROUP BY f.id, b.id;

这些是我得到的结果(注意count不正确):

foo.id    | foo.name     | count       | array_agg
--------------------------------------------------
1         | one          | 2           | {a, b}
2         | two          | 1           | {c}

始终是标签的count计数,而不是不同foo_bar值的计数。我已经尝试重新排序/修改返回不同结果但不是我正在寻找的结果的GROUP BY和子句。SELECT我认为我在使用该array_agg()功能时遇到了问题,但我不确定是否是这种情况或如何解决它。

4

1 回答 1

8
SELECT f.id, f.name, b.fb_ct, t.tag_names
FROM   foo f
LEFT JOIN  (
    SELECT foo_id AS id, count(*) AS fb_ct
    FROM   foo_bar
    GROUP  BY 1
    ) b USING (id)
LEFT JOIN  (
    SELECT target_id AS id, array_agg(name) AS tag_names
    FROM   tag
    GROUP  BY 1
    ) t USING (id)
ORDER  BY f.id;

产生所需的结果。

  • JOIN用显式语法重写。使其更易于阅读和理解(和调试)。

  • 通过加入多个1:n相关表,行将相互相乘,产生笛卡尔积——这是非常昂贵的废话。这是一个无意CROSS JOIN的代理。有关的:

  • 为避免这种情况,在聚合 ( ) 之前最多将一个 n-table加入 -table 。您可以聚合两次,但在将-table加入-table之前单独聚合它们会更干净、更快捷。1GROUP BYn1

  • 与您的原始版本(带有隐式INNER JOIN)相反。我LEFT JOIN用来避免丢失在orfoo中没有匹配行的行。foo_bartag

  • 一旦CROSS JOIN从查询中删除了意外,就不需要再添加DISTINCT了——假设它foo.id是唯一的。

于 2013-03-07T01:19:05.840 回答