PostgreSQL 9.2 row_to_json() avec imbriqué rejoint
Je suis en train de cartographier les résultats d'une requête JSON à l'aide de la row_to_json()
fonction qui a été ajoutée dans PostgreSQL 9.2.
Je vais avoir de la difficulté à déterminer la meilleure manière de représenter des lignes jointes comme des objets imbriqués (1:1 relations)
Voici ce que j'ai essayé (code d'installation: les tableaux, les données de l'échantillon, suivie par la requête):
-- some test tables to start out with:
create table role_duties (
id serial primary key,
name varchar
);
create table user_roles (
id serial primary key,
name varchar,
description varchar,
duty_id int, foreign key (duty_id) references role_duties(id)
);
create table users (
id serial primary key,
name varchar,
email varchar,
user_role_id int, foreign key (user_role_id) references user_roles(id)
);
DO $$
DECLARE duty_id int;
DECLARE role_id int;
begin
insert into role_duties (name) values ('Script Execution') returning id into duty_id;
insert into user_roles (name, description, duty_id) values ('admin', 'Administrative duties in the system', duty_id) returning id into role_id;
insert into users (name, email, user_role_id) values ('Dan', '[email protected]', role_id);
END$$;
La requête elle-même:
select row_to_json(row)
from (
select u.*, ROW(ur.*::user_roles, ROW(d.*::role_duties)) as user_role
from users u
inner join user_roles ur on ur.id = u.user_role_id
inner join role_duties d on d.id = ur.duty_id
) row;
J'ai découvert que si j'ai utilisé ROW()
, je pourrais séparer les champs qui en résultent dans un objet enfant, mais il semble limitée à un seul niveau. Je ne peux pas insérer plus de AS XXX
déclarations, je pense que j'en aurais besoin dans ce cas.
Je suis offerte les noms de colonne, parce que je l'ai jeté à la appropriée de type d'enregistrement, par exemple avec ::user_roles
, dans le cas de la table de résultats.
Voici ce que la requête retourne:
{
"id":1,
"name":"Dan",
"email":"[email protected]",
"user_role_id":1,
"user_role":{
"f1":{
"id":1,
"name":"admin",
"description":"Administrative duties in the system",
"duty_id":1
},
"f2":{
"f1":{
"id":1,
"name":"Script Execution"
}
}
}
}
Ce que je veux faire est de générer du JSON pour les jointures (encore 1:1 est très bien) dans une voie où je peux ajouter des jointures, et les ont représentés comme des objets enfants de parents ils rejoignent, c'est à dire comme ce qui suit:
{
"id":1,
"name":"Dan",
"email":"[email protected]",
"user_role_id":1,
"user_role":{
"id":1,
"name":"admin",
"description":"Administrative duties in the system",
"duty_id":1
"duty":{
"id":1,
"name":"Script Execution"
}
}
}
}
Toute aide est appréciée. Merci pour la lecture.
- C'est là, dans le code d'installation. Les inserts. Je suis allé à la peine de mettre tout en place afin que toute personne pourrait répliquer à ma situation.
Vous devez vous connecter pour publier un commentaire.
Mise à jour: Dans PostgreSQL 9.4 cela améliore beaucoup avec l'introduction de
to_json
,json_build_object
,json_object
etjson_build_array
, si c'est prolixe en raison de la nécessité de nommer tous les domaines explicitement:Pour les versions plus anciennes, lire sur.
Il n'est pas limité à une seule ligne, c'est juste un peu douloureux. Vous ne pouvez pas alias composite rowtypes à l'aide de
AS
, si vous avez besoin d'utiliser un alias sous-requête de l'expression ou de la CTE, pour obtenir l'effet:produit, via http://jsonprettyprint.com/:
Vous souhaitez utiliser
array_to_json(array_agg(...))
lorsque vous avez un 1:de nombreuses relations, btw.La requête ci-dessus devrait idéalement être en mesure d'être écrite comme:
... mais PostgreSQL est
ROW
constructeur n'accepte pasAS
les alias de colonne. Malheureusement.Heureusement, ils permettent d'optimiser le même. Comparer les plans:
LIGNE
constructeur de version avec les alias retiré, afin qu'il s'exécuteParce que les expressions de table communes sont l'optimisation de clôtures, de modifier le libellé du sous-requête imbriquée version à utiliser enchaîné d'expressions de table communes (
WITH
expressions) ne fonctionne pas bien, et ne se traduira pas dans le même plan. Dans ce cas, elles sont coincées avec des moches les sous-requêtes imbriquées jusqu'à ce que nous obtenir quelques améliorations àrow_to_json
ou un moyen de remplacer les noms de colonne dans unROW
constructeur plus directement.De toute façon, en général, le principe est que l'endroit où vous souhaitez créer un objet json avec des colonnes
a, b, c
, et vous souhaitez, vous pouvez simplement écrire la syntaxe illégale:place, vous pouvez utiliser des sous-requêtes scalaires de retour de la ligne des valeurs typées:
Ou:
En outre, gardez à l'esprit que vous pouvez composer
json
valeurs sans supplémentaires de citer, par exemple, si vous mettez de la sortie d'unjson_agg
dans unrow_to_json
, à l'intérieurjson_agg
résultat n'obtiendrez pas cité comme un string, ça va être intégrés directement en json.par exemple, dans l'arbitraire exemple:
la sortie est:
Noter que le
json_agg
produit,[{"a":1,"b":2}, {"a":1,"b":2}]
, n'a pas échappé à nouveau, commetext
serait.Cela signifie que vous pouvez composer json opérations de construction de lignes, vous n'avez toujours pas de créer extrêmement complexe PostgreSQL types de composé alors appel
row_to_json
sur la sortie.explain analyze
sur une vision plus réaliste de l'échantillon de données à voir.row_to_json(row(c1, c2, ...)::type_with_good_names)
) pour contourner la désagréable "sous-requête pour obtenir utile de la propriété des noms de" choses"? La requête en question n'est pas un one-off, mais c'était la seule chose en utilisant le type personnalisé.pg_type
entrée et un peu plus depg_attribute
entrées. Je ne voudrais pas le faire pour des milliers de requêtes, principalement parce qu'il aimerais obtenir ennuyeux à maintenir, mais c'est tout.json_build_object
va me rendre la vie beaucoup plus facile, mais de toute façon je n'ai pas ramasser sur elle quand j'ai vu les notes de version. Parfois, vous avez juste besoin d'un exemple concret pour vous aider à démarrer.json_build_object
un peu plus - c'est un véritable changeur de jeu.scalar subqueries returning row-typed values
? Je sais ce qu'est une sous-requête scalaire est, mais je ne pouvais pas trouver de la documentation/info. sur la partie de la requête qui dit":SELECT x FROM (...) x
x
est un tableau, alorsSELECT x FROM x LIMIT 1
renvoie une seule ligne de type de valeur. C'est scalaire, en ce qu'elle renvoie une seule valeur concrète. Mais cette valeur est un type composite, une ligne. Vous pouvez également utiliser la ligne de constructeur pour obtenir unrecord
résultat commeSELECT ROW(1,2,3)
Ma suggestion pour la facilité de maintenance sur le long terme est d'utiliser une VUE de construire le gros de la version de votre requête, puis utiliser une fonction comme ci-dessous:
Dans ce cas, l'objet de l'importance.les utilisateurs est un point de vue. Depuis que j'ai sélectionné les utilisateurs.*, Je ne vais pas avoir de mise à jour de cette fonction, si j'ai besoin de mettre à jour la vue pour inclure plus de champs dans un enregistrement d'utilisateur.