多対多の関係で結合を使用すると、結果は複数の行に分割されます。私がやりたいのは、結合の右側を配列に変換して、結果が1行になるようにすることです。
例は私がそれをよりよく説明します。
だから私はそれらの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)
);
タグの付いたアイテムを選択するときは、次のようにします。
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;
そして結果は次のようになります:
(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")
私が欲しいのはこれです:
(1, "item n1", ["sport", "soccer"])
(2, "item n2", ["adventure", "mountain climbing", "sport" , "nature"])
まず、クエリにタイプミスがあります。 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;
_
余談ですが、「id」または「title」は通常、あまり区別されず、あまり有用な識別子ではありません。詳細はこちら:
クエリのすべてまたはほとんどのアイテムを実行する場合、通常、最初に集計してから結合する方がかなり高速です。
_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);
_
_LEFT JOIN
_で除外されるタグのないアイテムがある場合は、外部クエリで_INNER JOIN
_を使用します。
結合before集約も、FROM
リストに1つ以上の1:nテーブルがあるため、手に負えなくなります(この単純なケースではありません)。比較:
小さな選択の場合、ARRAYコンストラクタを使用したLATERAL
結合を検討します:
_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;
_
ARRAYコンストラクタは常に行を生成するため(サブクエリが空の場合は空の配列で)、LEFT JOIN LATERAL (...) ON true
はここでは使用されません。
関連:
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,