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 di oggetti
Questa sezione descrive come generare un array JSON di oggetti, con ogni riga convertita in un singolo oggetto. Il risultato è simile a questo:
[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]
9.3 e versioni successive
La json_agg
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 esiste una versione jsonb
(introdotta nella 9.4) di json_agg
. Puoi aggregare le righe in un array e quindi convertirle:
SELECT to_jsonb(array_agg(t)) FROM t
o combinalo json_agg
con un cast:
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 le funzioni json_agg
o to_json
, quindi è necessario utilizzare la versione precedente array_to_json
:
SELECT array_to_json(array_agg(t)) FROM t
Facoltativamente, puoi includere una row_to_json
chiamata 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 è un array 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 la 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 quindi 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
Notare 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 iljson_build_object
funzione, consiglio vivamente di mettere una coppia chiave / valore su una riga per migliorare la leggibilità.
Potresti anche usare array_agg
al posto di json_agg
, ma i miei test indicano che json_agg
è leggermente più veloce.
Non esiste una jsonb
versione della 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 durante l'utilizzo to_jsonb
. Sospetto che ciò sia dovuto all'overhead dell'analisi e alla convalida del risultato JSON di json_agg
.
Oppure puoi usare un cast esplicito:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)::jsonb
FROM t
La to_jsonb
versione consente di evitare il cast ed è più veloce, secondo i miei test; di nuovo, sospetto che ciò sia dovuto al sovraccarico dell'analisi e della convalida del risultato.
9.4 e 9.3
La json_build_object
funzione era nuova nella 9.5, quindi devi 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, non to_json
esiste nemmeno . 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 .
Design
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 dall'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 fuori dagli schemi, l'unica ragione per cui posso pensare di non usarlo sarebbero considerazioni sulle prestazioni. Non reinventare la ruota e tutto il resto.
Null
Le funzioni aggregate in genere restituiscono NULL
quando operano su zero righe. Se questa è una possibilità, potresti volerla usare COALESCE
per evitarli. Un paio di esempi:
SELECT COALESCE(json_agg(t), '[]'::json) FROM t
O
SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t
Ringraziamo Hannes Landeholm per averlo sottolineato