web-dev-qa-db-fra.com

convertir le côté droit de la jointure de plusieurs en plusieurs dans un tableau

Lorsque vous utilisez la jointure sur plusieurs à plusieurs, le résultat est divisé sur plusieurs lignes. Ce que j'aimerais faire, c'est convertir le côté droit d'une jointure en un tableau pour que le résultat soit une ligne.

Un exemple l'expliquera mieux que moi.

J'ai donc ces 3 tableaux:

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)
);

Lorsque je sélectionne des éléments avec leurs balises, je peux le faire de cette façon:

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;

Et le résultat se présentera comme:

(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")

Ce que j'aimerais avoir, c'est ceci:

(1, "item n1", ["sport", "soccer"])
(2, "item n2", ["adventure", "mountain climbing", "sport" , "nature"])
13
Ced

Tout d'abord, vous avez une faute de frappe dans votre requête. La troisième colonne serait t.title. J'ai ajouté des alias pour clarifier:

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;

À part: "id" ou "title" ne sont généralement pas des identifiants très distinctifs et peu utiles. Plus ici:

Lors de l'interrogation de la totalité ou de la plupart des éléments , il est généralement beaucoup plus rapide d'agréger d'abord puis de rejoindre:

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);

Utilisez LEFT JOIN Dans la requête externe s'il peut y avoir des éléments sans balises, qui sont exclus avec un INNER JOIN.

Rejoindre avant l'agrégation devient également incontrôlable avec plus d'une table 1: n dans la liste FROM (pas dans ce cas simple). Comparer:

Pour une petite sélection , je considérerais une jointure LATERAL avec un constructeur 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;

Puisque le constructeur ARRAY produit toujours une ligne (avec un tableau vide si la sous-requête est vide), LEFT JOIN LATERAL (...) ON true n'est d'aucune utilité ici.

En relation:

15

Vous devez ajouter le group by clause et utiliser 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,
5
Evan Carroll