PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL renvoie le jeu de résultats en tant que tableau JSON ?

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