TL;DR
SELECT json_agg(t) FROM t
per una matrice JSON di oggetti e
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
per un oggetto JSON di array.
Elenco oggetti
Questa sezione descrive come generare una matrice JSON di oggetti, con ogni riga convertita in un singolo oggetto. Il risultato è simile al seguente:
[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]
9.3 e versioni successive
Il json_agg
la funzione produce questo risultato fuori dagli schemi. Capisce automaticamente come convertire il suo input in JSON e lo aggrega in un array.
SELECT json_agg(t) FROM t
Non ci sono jsonb
(introdotta nella versione 9.4) di json_agg
. Puoi aggregare le righe in un array e poi convertirle:
SELECT to_jsonb(array_agg(t)) FROM t
oppure combina json_agg
con un calco:
SELECT json_agg(t)::jsonb FROM t
I miei test suggeriscono che aggregarli prima in un array è un po' più veloce. Sospetto che ciò sia dovuto al fatto che il cast deve analizzare l'intero risultato JSON.
9.2
9.2 non ha il json_agg
o to_json
funzioni, quindi è necessario utilizzare il vecchio array_to_json
:
SELECT array_to_json(array_agg(t)) FROM t
Puoi opzionalmente includere un row_to_json
chiama nella query:
SELECT array_to_json(array_agg(row_to_json(t))) FROM t
Questo converte ogni riga in un oggetto JSON, aggrega gli oggetti JSON come un array e quindi converte l'array in un array JSON.
Non sono riuscito a distinguere alcuna differenza significativa di prestazioni tra i due.
Oggetto delle liste
Questa sezione descrive come generare un oggetto JSON, in cui ogni chiave è una colonna nella tabella e ogni valore è una matrice dei valori della colonna. È il risultato che assomiglia a questo:
{"a":[1,2,3], "b":["value1","value2","value3"]}
9.5 e versioni successive
Possiamo sfruttare json_build_object
funzione:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
Puoi anche aggregare le colonne, creando una singola riga, e poi convertirla in un oggetto:
SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
Nota che l'aliasing degli array è assolutamente necessario per garantire che l'oggetto abbia i nomi desiderati.
Quale sia più chiaro è una questione di opinione. Se si utilizza json_build_object
funzione, consiglio vivamente di inserire una coppia chiave/valore su una riga per migliorare la leggibilità.
Puoi anche usare array_agg
al posto di json_agg
, ma il mio test indica che json_agg
è leggermente più veloce.
Non ci sono jsonb
versione del json_build_object
funzione. Puoi aggregare in una singola riga e convertire:
SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
A differenza delle altre query per questo tipo di risultato, array_agg
sembra essere un po' più veloce quando si usa to_jsonb
. Sospetto che ciò sia dovuto all'analisi dell'overhead e alla convalida del risultato JSON di json_agg
.
Oppure puoi utilizzare un cast esplicito:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)::jsonb
FROM t
Il to_jsonb
la versione permette di evitare il cast ed è più veloce, secondo i miei test; ancora una volta, sospetto che ciò sia dovuto al sovraccarico dell'analisi e della convalida del risultato.
9.4 e 9.3
Il json_build_object
la funzione era nuova alla 9.5, quindi è necessario aggregare e convertire in un oggetto nelle versioni precedenti:
SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
o
SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
a seconda che tu voglia json
o jsonb
.
(9.3 non ha jsonb
.)
9.2
In 9.2, nemmeno to_json
esiste. Devi usare row_to_json
:
SELECT row_to_json(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
Documentazione
Trova la documentazione per le funzioni JSON nelle funzioni JSON.
json_agg
si trova nella pagina delle funzioni aggregate.
Progettazione
Se le prestazioni sono importanti, assicurati di confrontare le tue query con il tuo schema e i tuoi dati, piuttosto che fidarti dei miei test.
Che si tratti di un buon design o meno, dipende davvero dalla tua applicazione specifica. In termini di manutenibilità, non vedo alcun problema particolare. Semplifica il codice dell'app e significa che c'è meno da mantenere in quella parte dell'app. Se PG può darti esattamente il risultato di cui hai bisogno, l'unico motivo per cui mi viene in mente di non usarlo sarebbe considerazioni sulle prestazioni. Non reinventare la ruota e tutto il resto.
Nulli
Le funzioni aggregate in genere restituiscono NULL
quando operano su zero righe. Se questa è una possibilità, potresti voler usare COALESCE
per evitarli. Un paio di esempi:
SELECT COALESCE(json_agg(t), '[]'::json) FROM t
Oppure
SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t
Ringraziamento a Hannes Landeholm per averlo fatto notare