Aktualizace:V PostgreSQL 9.4 se to hodně zlepšilo zavedením to_json
, json_build_object
, json_object
a json_build_array
, i když je to podrobné kvůli nutnosti explicitně pojmenovat všechna pole:
select
json_build_object(
'id', u.id,
'name', u.name,
'email', u.email,
'user_role_id', u.user_role_id,
'user_role', json_build_object(
'id', ur.id,
'name', ur.name,
'description', ur.description,
'duty_id', ur.duty_id,
'duty', json_build_object(
'id', d.id,
'name', d.name
)
)
)
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;
Pro starší verze čtěte dále.
Není to omezeno na jeden řádek, je to jen trochu bolestivé. Pomocí AS
nelze vytvořit alias složených typů řádků , takže k dosažení efektu musíte použít výraz poddotazu s aliasem nebo CTE:
select row_to_json(row)
from (
select u.*, urd AS user_role
from users u
inner join (
select ur.*, d
from user_roles ur
inner join role_duties d on d.id = ur.duty_id
) urd(id,name,description,duty_id,duty) on urd.id = u.user_role_id
) row;
vyrábí prostřednictvím http://jsonprettyprint.com/:
{
"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"
}
}
}
Budete chtít použít array_to_json(array_agg(...))
když máte vztah 1:mnoho, btw.
Výše uvedený dotaz by v ideálním případě měl být možné napsat jako:
select row_to_json(
ROW(u.*, ROW(ur.*, d AS duty) 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;
... ale ROW
PostgreSQL konstruktor nepřijímá AS
aliasy sloupců. Bohužel.
Naštěstí se optimalizují stejně. Porovnejte plány:
- Verze vnořeného poddotazu; vs
- Ten druhý vnořený
ROW
verze konstruktoru s odstraněnými aliasy, takže se spustí
Protože CTE jsou optimalizační ploty, přeformulování verze vnořeného poddotazu tak, aby používala zřetězené CTE (WITH
výrazy) nemusí fungovat stejně dobře a nepovede ke stejnému plánu. V tomto případě jste uvízli u ošklivých vnořených poddotazů, dokud nezískáme nějaká vylepšení row_to_json
nebo způsob, jak přepsat názvy sloupců v ROW
konstruktor více přímo.
Každopádně obecně platí princip, že tam, kde chcete vytvořit objekt json se sloupci a, b, c
a přejete si, abyste mohli napsat ilegální syntaxi:
ROW(a, b, c) AS outername(name1, name2, name3)
místo toho můžete použít skalární poddotazy vracející hodnoty typu řádků:
(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername
Nebo:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername
Kromě toho mějte na paměti, že můžete vytvořit json
hodnoty bez dodatečného uvozování, např. pokud vložíte výstup json_agg
v rámci row_to_json
, vnitřní json_agg
výsledek nebude citován jako řetězec, bude začleněn přímo jako json.
např. v libovolném příkladu:
SELECT row_to_json(
(SELECT x FROM (SELECT
1 AS k1,
2 AS k2,
(SELECT json_agg( (SELECT x FROM (SELECT 1 AS a, 2 AS b) x) )
FROM generate_series(1,2) ) AS k3
) x),
true
);
výstup je:
{"k1":1,
"k2":2,
"k3":[{"a":1,"b":2},
{"a":1,"b":2}]}
Všimněte si, že json_agg
produkt, [{"a":1,"b":2}, {"a":1,"b":2}]
, nebyl znovu escapován jako text
by bylo.
To znamená, že můžete skládat json operacemi pro konstrukci řádků, nemusíte vždy vytvářet extrémně složité složené typy PostgreSQL a poté volat row_to_json
na výstupu.