Opdatering:I PostgreSQL 9.4 forbedres dette meget med introduktionen af to_json
, json_build_object
, json_object
og json_build_array
, selvom det er ordrigt på grund af behovet for at navngive alle felterne eksplicit:
vælg 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 ) ) )fra brugere uinner join user_roles ur on ur.id =u.user_role_idnner join role_duties d on d.id =ur.duty_id;
For ældre versioner, læs videre.
Det er ikke begrænset til en enkelt række, det er bare en smule smertefuldt. Du kan ikke kalde sammensatte rækketyper ved at bruge AS
, så du skal bruge et aliaseret underforespørgselsudtryk eller CTE for at opnå effekten:
vælg row_to_json(row)from ( vælg u.*, urd AS user_role fra brugere u indre joinforbindelse ( vælg ur.*, d fra user_rolles ur indre join role_duties d on d.id =ur.duty_id ) urd (id,name,description,duty_id,duty) på urd.id =u.user_role_id) række;
producerer, via http://jsonprettyprint.com/:
{ "id":1, "name":"Dan", "email":"[email protected]", "user_role_id":1, "user_role":{ "id":1, "name":"admin", "description":"Administrative opgaver i systemet", "duty_id":1, "duty":{ "id":1, "name":"Scriptudførelse" } }}
Du vil gerne bruge array_to_json(array_agg(...))
når man har et 1:mange forhold, btw.
Ovenstående forespørgsel bør ideelt set kunne skrives som:
vælg row_to_json( ROW(u.*, ROW(ur.*, d AS duty) AS user_role))from users uinner join user_roles ur on ur.id =u.user_role_dinner join role_duties d on d.id =ur.duty_id;
... men PostgreSQL's ROW
konstruktør accepterer ikke AS
kolonnealiasser. Desværre.
Heldigvis optimerer de det samme. Sammenlign planerne:
- Den indlejrede underforespørgselversion; vs.
- Sidstnævnte indlejrede
ROW
konstruktørversion med aliasserne fjernet, så den udføres
Fordi CTE'er er optimeringshegn, omformulerer den indlejrede underforespørgselsversion til at bruge kædede CTE'er (WITH
udtryk) fungerer muligvis ikke så godt og vil ikke resultere i den samme plan. I dette tilfælde sidder du lidt fast med grimme indlejrede underforespørgsler, indtil vi får nogle forbedringer til row_to_json
eller en måde at tilsidesætte kolonnenavnene i en ROW
konstruktør mere direkte.
Generelt er princippet i hvert fald, at hvor du vil oprette et json-objekt med kolonner a, b, c
, og du ville ønske, at du bare kunne skrive den ulovlige syntaks:
ROW(a, b, c) AS ydernavn(navn1, navn2, navn3)
du kan i stedet bruge skalære underforespørgsler, der returnerer rækketypeværdier:
(SELECT x FROM (VÆLG a AS navn1, b AS navn2, c AS navn3) x) AS ydernavn
Eller:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS ydernavn
Derudover skal du huske på, at du kan komponere json
værdier uden yderligere citering, f.eks. hvis du sætter output fra en json_agg
inden for en row_to_json
, den indre json_agg
Resultatet bliver ikke citeret som en streng, det vil blive indarbejdet direkte som json.
for eksempel. i det vilkårlige eksempel:
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 gener_series(1,2) ) ) AS k3 ) x), sand);
outputtet er:
{"k1":1, "k2":2, "k3":[{"a":1,"b":2}, {"a":1,"b":2 }]}
Bemærk, at json_agg
produkt, [{"a":1,"b":2}, {"a":1,"b":2}]
, er ikke blevet escaped igen, som tekst
ville være.
Det betyder, at du kan skrive json-operationer for at konstruere rækker, behøver du ikke altid at skabe enormt komplekse PostgreSQL-sammensatte typer og derefter kalde row_to_json
på udgangen.