TL;DR
SELECT json_agg(t) FROM t
for en JSON-array af objekter og
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
for et JSON-objekt af arrays.
Liste over objekter
Dette afsnit beskriver, hvordan man genererer et JSON-array af objekter, hvor hver række konverteres til et enkelt objekt. Resultatet ser således ud:
[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]
9.3 og op
json_agg
funktion producerer dette resultat ud af boksen. Den finder automatisk ud af, hvordan dens input konverteres til JSON og samler den til et array.
SELECT json_agg(t) FROM t
Der er ingen jsonb
(introduceret i 9.4) version af json_agg
. Du kan enten aggregere rækkerne til en matrix og derefter konvertere dem:
SELECT to_jsonb(array_agg(t)) FROM t
eller kombiner json_agg
med en rollebesætning:
SELECT json_agg(t)::jsonb FROM t
Min test tyder på, at det først er lidt hurtigere at samle dem i et array. Jeg formoder, at dette skyldes, at castet skal analysere hele JSON-resultatet.
9,2
9.2 har ikke json_agg
eller to_json
funktioner, så du skal bruge den ældre array_to_json
:
SELECT array_to_json(array_agg(t)) FROM t
Du kan eventuelt inkludere en row_to_json
ring ind forespørgslen:
SELECT array_to_json(array_agg(row_to_json(t))) FROM t
Dette konverterer hver række til et JSON-objekt, samler JSON-objekterne som et array og konverterer derefter arrayet til et JSON-array.
Jeg var ikke i stand til at skelne nogen væsentlig forskel i ydeevnen mellem de to.
Objekt med lister
Dette afsnit beskriver, hvordan man genererer et JSON-objekt, hvor hver nøgle er en kolonne i tabellen, og hver værdi er en matrix af værdierne i kolonnen. Det er resultatet, der ser sådan ud:
{"a":[1,2,3], "b":["value1","value2","value3"]}
9,5 og op
Vi kan udnytte json_build_object
funktion:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
Du kan også samle kolonnerne, oprette en enkelt række og derefter konvertere den til et objekt:
SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
Bemærk, at aliasing af arrays er absolut nødvendig for at sikre, at objektet har de ønskede navne.
Hvilken en der er tydeligst er et spørgsmål om mening. Hvis du bruger json_build_object
funktion, anbefaler jeg stærkt at sætte ét nøgle/værdi-par på en linje for at forbedre læsbarheden.
Du kan også bruge array_agg
i stedet for json_agg
, men min test viser, at json_agg
er lidt hurtigere.
Der er ingen jsonb
version af json_build_object
fungere. Du kan samle til en enkelt række og konvertere:
SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
I modsætning til de andre forespørgsler for denne slags resultater, array_agg
ser ud til at være lidt hurtigere, når du bruger to_jsonb
. Jeg formoder, at dette skyldes overhead-parsing og validering af JSON-resultatet af json_agg
.
Eller du kan bruge en eksplicit rollebesætning:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)::jsonb
FROM t
to_jsonb
version giver dig mulighed for at undgå rollebesætningen og er hurtigere, ifølge min test; igen, jeg formoder, at dette skyldes overhead ved parsing og validering af resultatet.
9.4 og 9.3
json_build_object
funktion var ny i 9.5, så du skal aggregere og konvertere til et objekt i tidligere versioner:
SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
eller
SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
afhængig af om du vil have json
eller jsonb
.
(9.3 har ikke jsonb
.)
9,2
I 9.2, ikke engang to_json
eksisterer. Du skal bruge 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
Dokumentation
Find dokumentationen til JSON-funktionerne i JSON-funktioner.
json_agg
er på siden for samlede funktioner.
Design
Hvis ydeevne er vigtig, skal du sikre dig, at du benchmarker dine forespørgsler mod dit eget skema og dine data i stedet for at stole på min test.
Om det er et godt design eller ej, afhænger virkelig af din specifikke anvendelse. Med hensyn til vedligeholdelse ser jeg ikke noget særligt problem. Det forenkler din app-kode og betyder, at der er mindre at vedligeholde i den del af appen. Hvis PG kan give dig præcis det resultat, du har brug for ud af boksen, er den eneste grund, jeg kan komme i tanke om til ikke at bruge det, ydelsesovervejelser. Lad være med at genopfinde hjulet og det hele.
Nuller
Aggregerede funktioner giver typisk NULL
tilbage når de kører over nul rækker. Hvis dette er en mulighed, vil du måske bruge COALESCE
at undgå dem. Et par eksempler:
SELECT COALESCE(json_agg(t), '[]'::json) FROM t
Eller
SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t
Til ære til Hannes Landeholm for at påpege dette