web-dev-qa-db-fra.com

PostgreSQL renvoie le résultat en tant que tableau JSON?

J'aimerais que PostgreSQL renvoie le résultat d'une requête sous la forme d'un tableau JSON. Donné

create table t (a int primary key, b text);

insert into t values (1, 'value1');
insert into t values (2, 'value2');
insert into t values (3, 'value3');

Je voudrais quelque chose de semblable à

[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]

ou

{"a":[1,2,3], "b":["value1","value2","value3"]}

(en fait, il serait plus utile de connaître les deux). J'ai essayé des choses comme

select row_to_json(row) from (select * from t) row;
select array_agg(row) from (select * from t) row;
select array_to_string(array_agg(row), '') from (select * from t) row;

Et je sens que je suis proche, mais pas vraiment là-bas. Dois-je consulter d'autres documents, à l'exception de 9.15. Fonctions et opérateurs JSON ?

À propos, je ne suis pas sûr de mon idée. Est-ce une décision de conception habituelle? Ma pensée est que je pourrais bien sûr prendre le résultat (par exemple) de la première des 3 requêtes ci-dessus et le manipuler légèrement dans l'application avant de le servir au client, mais si PostgreSQL peut créer directement l'objet JSON final, ce serait plus simple, car je n'ai toujours pas intégré de dépendance à une bibliothèque JSON dans mon application.

96
engineerX

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 d'objets

Cette section explique 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 fonction json_agg produit ce résultat immédiatement. Il détermine automatiquement comment convertir ses entrées en JSON et les agrège dans un tableau.

SELECT json_agg(t) FROM t

Il n'y a pas de version jsonb (introduite dans la version 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 associez json_agg avec une distribution:

SELECT json_agg(t)::jsonb FROM t

Mes tests suggèrent que l'agréger dans un tableau est un peu plus rapide. Je suppose que cela est dû au fait que le casting doit analyser le résultat JSON dans son intégralité.

9.2

9.2 n'a pas les fonctions json_agg ou to_json, vous devez donc utiliser l'ancien array_to_json:

SELECT array_to_json(array_agg(t)) FROM t

Vous pouvez éventuellement inclure un appel row_to_json dans la requête:

SELECT array_to_json(array_agg(row_to_json(t))) FROM t

Cela convertit chaque ligne en objet JSON, regroupe les objets JSON sous forme de tableau, puis convertit le tableau en tableau JSON.

Je n'ai pas pu discerner de différence de performance significative entre les deux.

Objet de listes

Cette section explique comment générer un objet JSON, chaque clé étant une colonne de la table et chaque valeur 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 utiliser la fonction json_build_object:

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 les convertir en objets:

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 porte les noms souhaités.

Lequel est le plus clair est une question d'opinion. Si vous utilisez la fonction json_build_object, il est vivement recommandé de placer 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 version jsonb de la fonction json_build_object. 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 lorsque vous utilisez to_jsonb. Je soupçonne que cela est dû à la surcharge d'analyse 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 version to_jsonb vous permet d'éviter la distribution et est plus rapide, selon mes tests; encore une fois, je soupçonne que cela est dû à une surcharge d’analyse et de validation du résultat.

9.4 et 9.3

La fonction json_build_object étant nouvelle pour la version 9.5, vous devez regrouper 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, même to_json n'existe 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 relative aux fonctions JSON dans fonctions JSON .

json_agg est sur la page fonctions d'agrégation .

Conception

Si les performances sont importantes, assurez-vous de comparer vos requêtes à votre propre schéma et à vos données, plutôt que de faire confiance à mes tests.

Que ce soit 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 de maintenance dans cette partie de l'application. Si PG peut vous donner exactement le résultat dont vous avez besoin, la seule raison pour laquelle je peux penser à ne pas l'utiliser serait des considérations de performances. Ne réinventez pas la roue et tout.

Nulls

Les fonctions d'agrégation rendent généralement NULL lorsqu'elles fonctionnent sur zéro ligne. Si cela est possible, 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

Nous remercions Hannes Landeholm pour le signalant

185
jpmc26

Aussi, si vous voulez sélectionner le champ de la table et l'agréger alors comme tableau.

SELECT json_agg(json_build_object('data_a',a,
                                  'data_b',b,
))  from t;

Le résultat viendra.

 [{'data_a':1,'data_b':'value1'}
  {'data_a':2,'data_b':'value2'}]
12
Himanshu sharma