TL;DR
SELECT json_agg(t) FROM t
för en JSON-array av objekt och
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
för ett JSON-objekt med arrayer.
Lista över objekt
Det här avsnittet beskriver hur man genererar en JSON-array av objekt, där varje rad konverteras till ett enda objekt. Resultatet ser ut så här:
[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]
9.3 och uppåt
json_agg
funktionen ger detta resultat direkt. Den räknar automatiskt ut hur den konverterar sin indata till JSON och aggregerar den till en array.
SELECT json_agg(t) FROM t
Det finns ingen jsonb
(introducerad i 9.4) version av json_agg
. Du kan antingen aggregera raderna till en array och sedan konvertera dem:
SELECT to_jsonb(array_agg(t)) FROM t
eller kombinera json_agg
med en skådespelare:
SELECT json_agg(t)::jsonb FROM t
Mina tester tyder på att det går lite snabbare att aggregera dem i en array först. Jag misstänker att detta beror på att casten måste analysera hela JSON-resultatet.
9.2
9.2 har inte json_agg
eller to_json
funktioner, så du måste använda den äldre array_to_json
:
SELECT array_to_json(array_agg(t)) FROM t
Du kan valfritt inkludera en row_to_json
ring in frågan:
SELECT array_to_json(array_agg(row_to_json(t))) FROM t
Detta konverterar varje rad till ett JSON-objekt, aggregerar JSON-objekten som en array och konverterar sedan arrayen till en JSON-array.
Jag kunde inte urskilja någon betydande prestandaskillnad mellan de två.
Föremål för listor
Det här avsnittet beskriver hur man genererar ett JSON-objekt, där varje nyckel är en kolumn i tabellen och varje värde är en matris av värdena i kolumnen. Det är resultatet som ser ut så här:
{"a":[1,2,3], "b":["value1","value2","value3"]}
9,5 och uppåt
Vi kan utnyttja json_build_object
funktion:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
Du kan också aggregera kolumnerna, skapa en enda rad och sedan konvertera den till ett objekt:
SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
Observera att aliasing av arrayerna är absolut nödvändigt för att säkerställa att objektet har de önskade namnen.
Vilken som är tydligare är en åsiktsfråga. Om du använder json_build_object
funktion rekommenderar jag starkt att du lägger ett nyckel/värdepar på en rad för att förbättra läsbarheten.
Du kan också använda array_agg
i stället för json_agg
, men mina tester visar att json_agg
är något snabbare.
Det finns ingen jsonb
version av json_build_object
fungera. Du kan slå samman till en enda rad och konvertera:
SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
Till skillnad från de andra frågorna för den här typen av resultat, array_agg
verkar vara lite snabbare när du använder to_jsonb
. Jag misstänker att detta beror på overheadanalys och validering av JSON-resultatet för json_agg
.
Eller så kan du använda en explicit rollbesättning:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)::jsonb
FROM t
to_jsonb
versionen låter dig undvika rollbesättningen och är snabbare, enligt mina tester; återigen, jag misstänker att detta beror på overhead för att analysera och validera resultatet.
9.4 och 9.3
json_build_object
funktion var ny för 9.5, så du måste aggregera och konvertera till ett objekt i tidigare 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
beroende på om du vill ha json
eller jsonb
.
(9.3 har inte jsonb
.)
9.2
I 9.2, inte ens to_json
existerar. Du måste använda 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
Hitta dokumentationen för JSON-funktionerna i JSON-funktioner.
json_agg
finns på sidan för sammanställda funktioner.
Design
Om prestanda är viktigt, se till att du jämför dina frågor mot ditt eget schema och dina egna data, istället för att lita på mina tester.
Om det är en bra design eller inte beror verkligen på din specifika applikation. När det gäller underhållbarhet ser jag inget speciellt problem. Det förenklar din appkod och betyder att det finns mindre att underhålla i den delen av appen. Om PG kan ge dig exakt det resultat du behöver ur lådan, skulle den enda anledningen jag kan komma på att inte använda det vara prestandaöverväganden. Uppfinn inte hjulet igen och allt.
Null
Aggregatfunktioner ger vanligtvis tillbaka NULL
när de fungerar över noll rader. Om detta är en möjlighet kanske du vill använda COALESCE
att undvika dem. Ett par exempel:
SELECT COALESCE(json_agg(t), '[]'::json) FROM t
Eller
SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t
Tack till Hannes Landeholm för att han påpekade detta