将多对多连接的右侧转换为数组

Ced*_*Ced 26 postgresql aggregate array postgresql-9.4

在多对多关系上使用 join 时,结果会拆分为多行。我想做的是将连接的右侧转换为数组,因此结果是一行。

包含 3 个表的示例:

CREATE TABLE items (
  id    serial primary key,
  title text
);

CREATE TABLE tags (
  id    serial primary key,
  title text
);

CREATE TABLE items_tags (
  item_id int references items(id),
  tag_id  int references tags(id),
  primary key (item_id, tag_id)
);
Run Code Online (Sandbox Code Playgroud)

选择带有标签的项目时,我可以这样做:

SELECT i.id, i.title, i.title
FROM items i
INNER JOIN items_tags it
ON it.item_id = i.id
INNER JOIN tags t
ON t.id = it.tag_id;
Run Code Online (Sandbox Code Playgroud)

结果将显示为:

(1, "item n1", "sport")
(1, "item n1", "soccer")
(2, "item n2", "adventure")
(2, "item n2", "mountain climbing")
(2, "item n2", "sport")
(2, "item n2", "nature")
Run Code Online (Sandbox Code Playgroud)

我想要的是这样的:

(1, "item n1", ["sport", "soccer"])
(2, "item n2", ["adventure", "mountain climbing", "sport" , "nature"])
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 27

聚合大多数行

在查询所有或大多数项目时,首先聚合“许多”表中的行然后再加入通常要快得多:

SELECT id, i.title AS item_title, t.tag_array
FROM   items      i
JOIN  (  -- or LEFT JOIN ?
   SELECT it.item_id AS id, array_agg(t.title) AS tag_array
   FROM   items_tags it
   JOIN   tags       t  ON t.id = it.tag_id
   GROUP  BY it.item_id
   ) t USING (id);
Run Code Online (Sandbox Code Playgroud)

LEFT [OUTER] JOIN如果可以有没有标签的项目,则在外部查询中使用- 将使用[INNER] JOIN.

由于这不会将连接中的行相乘,因此我们不需要GROUP BY在外部SELECT.

如果列表中有多个 1:n 表(不是在这种简单的情况下),则聚合之前加入也会失控FROM。看:

聚合几行

对于一小部分行,使用LATERAL带有ARRAY 构造函数的连接

SELECT id, title AS item_title, t.tag_array
FROM   items i, LATERAL (  -- this is an implicit CROSS JOIN
   SELECT ARRAY (
      SELECT t.title
      FROM   items_tags it
      JOIN   tags       t  ON t.id = it.tag_id
      WHERE  it.item_id = i.id
      ) AS tag_array
   ) t;
Run Code Online (Sandbox Code Playgroud)

由于 ARRAY 构造函数总是生成一行(如果子查询为空,则为空数组 - 结果中的细微差别!),LEFT JOIN LATERAL (...) ON true这里不需要。看:

在旁边

您的查询中有错字。第 3 列将是t.title. 我在您的原始(未聚合)查询中添加了别名以澄清:

SELECT i.id, i.title AS item_title, t.title AS tag_title
FROM   items      i
JOIN   items_tags it ON it.item_id = i.id
JOIN   tags       t  ON t.id = it.tag_id;
Run Code Online (Sandbox Code Playgroud)

“id”或“title”通常不是很独特也不是很有用的标识符。看:


Eva*_*oll 16

您需要添加group by子句并使用array_agg.

SELECT i.id, i.title, array_agg(i.title)
FROM items i
INNER JOIN items_tags it
ON it.item_id = i.id
INNER JOIN tags t
ON t.id = it.tag_id
GROUP BY i.id, i.title,
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

32629 次

最近记录:

4 年,11 月 前