J'ai une colonne data
qui contient un json
document à peu près comme ceci:
{
"name": "foo",
"tags": ["foo", "bar"]
}
Je voudrais transformer le tags
tableau imbriqué en une chaîne concaténée ( foo, bar
). Ce serait facilement possible avec la array_to_string()
fonction en théorie. Cependant, cette fonction n'agit pas sur les json
tableaux. Je me demande donc comment transformer ce json
tableau en Postgres array
?
postgresql
postgresql-9.3
array
json
Christoph
la source
la source
json_extract_path_text(your_column, 'tags')
ce que vous cherchez?Réponses:
Postgres 9.4 ou plus récent
Visiblement inspiré par ce billet , Postgres 9.4 a ajouté la (les) fonction (s) manquante (s):
Merci à Laurence Rowe pour le correctif et Andrew Dunstan pour son engagement!
json_array_elements_text(json)
jsonb_array_elements_text(jsonb)
Pour annuler le tableau JSON. Ensuite, utilisez
array_agg()
ou un constructeur ARRAY pour construire un tableau Postgres à partir de celui-ci. Oustring_agg()
pour construire unetext
chaîne .Agrégez les éléments non imbriqués par ligne dans une
LATERAL
sous-requête ou une sous-requête corrélée. Ensuite, l'ordre d'origine est conservé et nous n'avons pas besoinORDER BY
,GROUP BY
ni même d'une clé unique dans la requête externe. Voir:Remplacez "json" par "jsonb"
jsonb
dans tout le code SQL suivant.Syntaxe courte:
Apparenté, relié, connexe:
Constructeur ARRAY dans une sous-requête corrélée:
Apparenté, relié, connexe:
Différence subtile : les
null
éléments sont conservés dans des tableaux réels . Ceci n'est pas possible dans les requêtes ci-dessus produisant unetext
chaîne, qui ne peut pas contenir denull
valeurs. La vraie représentation est un tableau.Fonction wrapper
Pour une utilisation répétée, pour simplifier encore plus, encapsulez la logique dans une fonction:
Faites-en une fonction SQL , afin qu'il puisse être intégré dans des requêtes plus volumineuses.
Faites-le
IMMUTABLE
(parce que c'est le cas) pour éviter les évaluations répétées dans les requêtes plus volumineuses et autorisez-le dans les expressions d'index.Appel:
db <> fiddle ici
Postgres 9.3 ou plus ancien
Utilisez la fonction
json_array_elements()
. Mais nous en tirons une double chaîne .Requête alternative avec agrégation dans la requête externe.
CROSS JOIN
supprime les lignes avec des tableaux manquants ou vides. Peut aussi être utile pour traiter des éléments. Nous avons besoin d'une clé unique pour agréger:Le constructeur ARRAY, toujours avec les chaînes citées:
Notez que cela
null
est converti en la valeur de texte "null", contrairement à ci-dessus. Incorrect, à proprement parler, et potentiellement ambigu.Le pauvre homme sans scrupule avec
trim()
:Récupérer une seule ligne de tbl:
Les chaînes forment une sous-requête corrélée:
ARRAY constructeur:
Fiddle SQL original (obsolète) .
db <> fiddle ici.
Apparenté, relié, connexe:
Notes (obsolète depuis la page 9.4)
Nous aurions besoin de
json_array_elements_text(json)
, le jumeau dejson_array_elements(json)
pour renvoyer lestext
valeurs appropriées d'un tableau JSON. Mais cela semble manquer à l' arsenal fourni de fonctions JSON . Ou une autre fonction pour extraire unetext
valeur d'uneJSON
valeur scalaire . Il me semble qu'il me manque aussi celui-là.J'ai donc improvisé avec
trim()
, mais cela échouera pour des cas non triviaux ...la source
to_jsonb()
pour la conversion tableau-> jsonb.SELECT ARRAY(SELECT json_array_elements_text(_js))
cela garantit vraiment que l'ordre du tableau est préservé? L'optimiseur n'est-il pas autorisé à modifier théoriquement l'ordre des lignes sortant de json_array_elements_text?PG 9.4+
La réponse acceptée est certainement ce dont vous avez besoin, mais par souci de simplicité, voici une aide que j’utilise pour cela:
Alors fais juste:
la source
Cette question a été posée sur les listes de diffusion de PostgreSQL et j’ai imaginé cette méthode sophistiquée de conversion de texte JSON en type de texte PostgreSQL via l’opérateur d’extraction de champ JSON:
Fondamentalement, il convertit la valeur en un tableau à un seul élément, puis demande le premier élément.
Une autre approche consiste à utiliser cet opérateur pour extraire tous les champs un par un. Mais pour les grands tableaux, cela est probablement plus lent, car il faut analyser la chaîne JSON entière pour chaque élément du tableau, ce qui entraîne une complexité de O (n ^ 2).
la source
J'ai testé quelques options. Voici ma requête préférée. Supposons que nous ayons une table contenant les champs id et json. Le champ json contient un tableau que nous voulons transformer en tableau pg.
Il fonctionne n'importe où et plus vite que les autres, mais a l'air craquant.)
Tout d'abord, json array est converti en texte, puis nous changeons les crochets en parenthèses. Enfin, le texte est converti en tableau du type requis.
et si vous préférez les tableaux de texte []
la source
SELECT TRANSLATE('{"name": "foo", "tags": ["foo", "bar"]}'::jsonb::text, '[]','{}')::INT[]; ERROR: malformed array literal: "{"name": "foo", "tags": {"foo", "bar"}}"
Je pense que vous devez ajouter une explication sur la façon dont cela est censé fonctionner.SELECT translate('["foo", "bar"]'::jsonb::text, '[]','{}')::INT[]; ERROR: invalid input syntax for integer: "foo"
Ce n'est pas si à l'Ces quelques fonctions, tirées des réponses à cette question , sont ce que j'utilise et fonctionnent très bien.
Dans chacun d’eux, en concaténant avec un tableau vide, ils gèrent un cas qui m’a un peu troublé la tête, en ce sens que si vous essayez de lancer un tableau vide à partir de
json
/jsonb
sans, vous n’obtiendrez rien en retour. tableau vide ({}
) comme on peut s'y attendre. Je suis certain qu'il y a une optimisation pour eux, mais ils sont laissés en l'état pour simplifier l'explication du concept.la source