TL; DR
SELECT json_agg(t) FROM t
pour un tableau d'objets JSON, 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 d'objets JSON, 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 plus
La json_agg
fonction produit ce résultat hors de la boîte. Il détermine automatiquement comment convertir son entrée en JSON et l'agrège en un tableau.
SELECT json_agg(t) FROM t
Il n'y a pas de version jsonb
(introduite dans 9.4) 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 le casting doit analyser l'intégralité du résultat JSON.
9.2
9.2 n'a pas les fonctions json_agg
ou to_json
, vous devez donc utiliser l'ancienne array_to_json
:
SELECT array_to_json(array_agg(t)) FROM t
Vous pouvez éventuellement inclure un row_to_json
appel dans la requête:
SELECT array_to_json(array_agg(row_to_json(t))) FROM t
Cela convertit chaque ligne en objet JSON, agrège les objets JSON en tant que tableau, puis convertit le tableau en tableau JSON.
Je n'ai pas pu discerner de différence de performance significative entre les deux.
Objet des 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 tirer parti de la 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, créer 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 garantir que l'objet a les noms souhaités.
Laquelle est la plus claire est une question d’opinion. Si vous utilisez la 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
c'est légèrement plus rapide.
Il n'y a pas de jsonb
version de la json_build_object
fonction. Vous pouvez regrouper 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 to_jsonb
. Je soupçonne que cela est dû à une analyse des frais généraux et à la validation du résultat JSON de json_agg
.
Ou vous pouvez utiliser une distribution explicite:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)::jsonb
FROM t
La to_jsonb
version vous 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
La json_build_object
fonction était nouvelle dans la version 9.5, vous devez donc agréger et convertir en 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, to_json
n'existe même pas . 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
Documentation
Recherchez la documentation des fonctions JSON dans les 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.
Que ce soit une bonne conception ou pas 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 à maintenir dans cette partie de l'application. Si PG peut vous donner exactement le résultat dont vous avez besoin, la seule raison à laquelle je pense pour ne pas l'utiliser serait des considérations de performances. Ne réinventez pas la roue et tout.
Nulls
Les fonctions d'agrégation donnent généralement en retour NULL
lorsqu'elles fonctionnent sur zéro ligne. Si c'est une possibilité, vous voudrez peut-être l'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
Crédit à Hannes Landeholm pour avoir signalé