Aggiornamento:in PostgreSQL 9.4 questo migliora molto con l'introduzione di to_json
, json_build_object
, json_object
e json_build_array
, sebbene sia dettagliato a causa della necessità di nominare tutti i campi in modo esplicito:
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;
Per le versioni precedenti, continua a leggere.
Non è limitato a una singola riga, è solo un po' doloroso. Non puoi alias di tipi di riga compositi utilizzando AS
, quindi è necessario utilizzare un'espressione di sottoquery con alias o CTE per ottenere l'effetto:
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;
produce, tramite 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"
}
}
}
Dovrai usare array_to_json(array_agg(...))
quando hai una relazione 1:molti, a proposito.
La query precedente dovrebbe idealmente essere scritta come:
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;
... ma ROW
di PostgreSQL il costruttore non accetta AS
alias di colonna. Purtroppo.
Per fortuna, ottimizzano lo stesso. Confronta i piani:
- La versione nidificata della sottoquery; contro
- Quest'ultimo ha nidificato
ROW
versione del costruttore con gli alias rimossi in modo che venga eseguito
Poiché i CTE sono barriere di ottimizzazione, riformulare la versione della sottoquery nidificata per utilizzare CTE concatenati (WITH
espressioni) potrebbero non funzionare altrettanto bene e non risulteranno nello stesso piano. In questo caso sei un po' bloccato con brutte sottoquery nidificate finché non otteniamo alcuni miglioramenti a row_to_json
o un modo per sovrascrivere i nomi delle colonne in una ROW
costruttore in modo più diretto.
Comunque, in generale, il principio è quello dove vuoi creare un oggetto json con colonne a, b, c
e vorresti poter semplicemente scrivere la sintassi illegale:
ROW(a, b, c) AS outername(name1, name2, name3)
puoi invece utilizzare sottoquery scalari che restituiscono valori tipizzati per riga:
(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername
Oppure:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername
Inoltre, tieni presente che puoi comporre json
valori senza virgolette aggiuntive, ad es. se metti l'output di un json_agg
all'interno di un row_to_json
, il json_agg
interno result non verrà citato come una stringa, verrà incorporato direttamente come json.
per esempio. nell'esempio arbitrario:
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
);
l'output è:
{"k1":1,
"k2":2,
"k3":[{"a":1,"b":2},
{"a":1,"b":2}]}
Nota che il json_agg
prodotto, [{"a":1,"b":2}, {"a":1,"b":2}]
, non è stato più eseguito l'escape, come text
sarebbe.
Ciò significa che puoi comporre json per costruire righe, non è sempre necessario creare tipi compositi PostgreSQL estremamente complessi, quindi chiamare row_to_json
sull'uscita.