TL;DR
SELECT json_agg(t) FROM t
pour un tableau JSON d'objets, et
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
pour un objet JSON de tableaux.
Liste des objets
Cette section décrit comment générer un tableau JSON d'objets, chaque ligne étant convertie en un seul objet. Le résultat ressemble à ceci :
[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]
9.3 et versions ultérieures
Le json_agg
La fonction produit ce résultat prêt à l'emploi. Il détermine automatiquement comment convertir son entrée en JSON et l'agrège dans un tableau.
SELECT json_agg(t) FROM t
Il n'y a pas de jsonb
(introduit dans 9.4) version de json_agg
. Vous pouvez soit agréger les lignes dans un tableau, puis les convertir :
SELECT to_jsonb(array_agg(t)) FROM t
ou combiner json_agg
avec un casting :
SELECT json_agg(t)::jsonb FROM t
Mes tests suggèrent que les agréger d'abord dans un tableau est un peu plus rapide. Je soupçonne que c'est parce que la distribution doit analyser l'intégralité du résultat JSON.
9.2
9.2 n'a pas le json_agg
ou to_json
fonctions, vous devez donc utiliser l'ancien array_to_json
:
SELECT array_to_json(array_agg(t)) FROM t
Vous pouvez éventuellement inclure un row_to_json
appeler dans la requête :
SELECT array_to_json(array_agg(row_to_json(t))) FROM t
Cela convertit chaque ligne en un objet JSON, agrège les objets JSON sous forme de tableau, puis convertit le tableau en tableau JSON.
Je n'ai pas pu discerner de différence de performances significative entre les deux.
Objet de listes
Cette section décrit comment générer un objet JSON, chaque clé étant une colonne de la table et chaque valeur étant un tableau des valeurs de la colonne. C'est le résultat qui ressemble à ceci :
{"a":[1,2,3], "b":["value1","value2","value3"]}
9.5 et plus
Nous pouvons exploiter le json_build_object
fonction :
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
Vous pouvez également agréger les colonnes en créant une seule ligne, puis la convertir en objet :
SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
Notez que l'aliasing des tableaux est absolument nécessaire pour s'assurer que l'objet porte les noms souhaités.
Laquelle est la plus claire est une question d'opinion. Si vous utilisez le json_build_object
fonction, je recommande fortement de mettre une paire clé/valeur sur une ligne pour améliorer la lisibilité.
Vous pouvez également utiliser array_agg
à la place de json_agg
, mais mes tests indiquent que json_agg
est légèrement plus rapide.
Il n'y a pas de jsonb
version de json_build_object
une fonction. Vous pouvez agréger en une seule ligne et convertir :
SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
Contrairement aux autres requêtes pour ce type de résultat, array_agg
semble être un peu plus rapide lors de l'utilisation de to_jsonb
. Je soupçonne que cela est dû à l'analyse des frais généraux et à la validation du résultat JSON de json_agg
.
Ou vous pouvez utiliser un cast explicite :
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)::jsonb
FROM t
Le to_jsonb
version permet d'éviter le casting et est plus rapide, selon mes tests; encore une fois, je soupçonne que cela est dû à la surcharge de l'analyse et de la validation du résultat.
9.4 et 9.3
Le json_build_object
fonction était nouvelle dans la version 9.5, vous devez donc agréger et convertir en un objet dans les versions précédentes :
SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
ou
SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
selon que vous voulez json
ou jsonb
.
(9.3 n'a pas jsonb
.)
9.2
En 9.2, même pas to_json
existe. Vous devez utiliser row_to_json
:
SELECT row_to_json(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
Documents
Retrouvez la documentation des fonctions JSON dans Fonctions JSON.
json_agg
se trouve sur la page des fonctions d'agrégation.
Conception
Si les performances sont importantes, assurez-vous de comparer vos requêtes à votre propre schéma et à vos propres données, plutôt que de faire confiance à mes tests.
Qu'il s'agisse d'une bonne conception ou non dépend vraiment de votre application spécifique. En termes de maintenabilité, je ne vois pas de problème particulier. Cela simplifie le code de votre application et signifie qu'il y a moins à gérer dans cette partie de l'application. Si PG peut vous donner exactement le résultat dont vous avez besoin, la seule raison à laquelle je peux penser pour ne pas l'utiliser serait des considérations de performances. Ne réinventez pas la roue et tout.
Nuls
Les fonctions d'agrégation renvoient généralement NULL
lorsqu'ils fonctionnent sur zéro ligne. Si c'est une possibilité, vous pouvez utiliser COALESCE
pour les éviter. Quelques exemples :
SELECT COALESCE(json_agg(t), '[]'::json) FROM t
Ou
SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t
Merci à Hannes Landeholm d'avoir signalé cela