TL;DR
SELECT json_agg(t) FROM t
para uma matriz JSON de objetos e
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
para um objeto JSON de matrizes.
Lista de objetos
Esta seção descreve como gerar uma matriz JSON de objetos, com cada linha sendo convertida em um único objeto. O resultado fica assim:
[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]
9,3 e superior
O
json_agg
função produz esse resultado fora da caixa. Ele descobre automaticamente como converter sua entrada em JSON e a agrega em uma matriz. SELECT json_agg(t) FROM t
Não há
jsonb
(introduzido em 9.4) versão de json_agg
. Você pode agregar as linhas em uma matriz e depois convertê-las:SELECT to_jsonb(array_agg(t)) FROM t
ou combine
json_agg
com elenco:SELECT json_agg(t)::jsonb FROM t
Meus testes sugerem que agregá-los em um array primeiro é um pouco mais rápido. Suspeito que isso ocorra porque o elenco precisa analisar todo o resultado JSON.
9.2
9.2 não tem o
json_agg
ou to_json
funções, então você precisa usar o antigo array_to_json
:SELECT array_to_json(array_agg(t)) FROM t
Opcionalmente, você pode incluir um
row_to_json
ligue na consulta:SELECT array_to_json(array_agg(row_to_json(t))) FROM t
Isso converte cada linha em um objeto JSON, agrega os objetos JSON como uma matriz e, em seguida, converte a matriz em uma matriz JSON.
Não consegui discernir nenhuma diferença significativa de desempenho entre os dois.
Objeto de listas
Esta seção descreve como gerar um objeto JSON, com cada chave sendo uma coluna na tabela e cada valor sendo uma matriz dos valores da coluna. É o resultado que se parece com isso:
{"a":[1,2,3], "b":["value1","value2","value3"]}
9,5 e superior
Podemos aproveitar o
json_build_object
função:SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)
FROM t
Você também pode agregar as colunas, criando uma única linha e depois convertê-la em um objeto:
SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
Observe que o alias dos arrays é absolutamente necessário para garantir que o objeto tenha os nomes desejados.
Qual é mais claro é uma questão de opinião. Se estiver usando o
json_build_object
função, eu recomendo colocar um par chave/valor em uma linha para melhorar a legibilidade. Você também pode usar
array_agg
no lugar de json_agg
, mas meus testes indicam que json_agg
é um pouco mais rápido. Não há
jsonb
versão do json_build_object
função. Você pode agregar em uma única linha e converter:SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
Ao contrário das outras consultas para esse tipo de resultado,
array_agg
parece ser um pouco mais rápido ao usar to_jsonb
. Suspeito que isso se deva à análise de sobrecarga e à validação do resultado JSON de json_agg
. Ou você pode usar uma conversão explícita:
SELECT
json_build_object(
'a', json_agg(t.a),
'b', json_agg(t.b)
)::jsonb
FROM t
O
to_jsonb
versão permite que você evite o elenco e é mais rápido, de acordo com meus testes; novamente, suspeito que isso se deva à sobrecarga de análise e validação do resultado. 9,4 e 9,3
O
json_build_object
função era nova para 9.5, então você tem que agregar e converter para um objeto nas versões anteriores:SELECT to_json(r)
FROM (
SELECT
json_agg(t.a) AS a,
json_agg(t.b) AS b
FROM t
) r
ou
SELECT to_jsonb(r)
FROM (
SELECT
array_agg(t.a) AS a,
array_agg(t.b) AS b
FROM t
) r
dependendo se você deseja
json
ou jsonb
. (9.3 não tem
jsonb
.) 9.2
Em 9.2, nem mesmo
to_json
existe. Você deve usar 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
Documentação
Encontre a documentação das funções JSON em funções JSON.
json_agg
está na página de funções agregadas. Projeto
Se o desempenho for importante, certifique-se de comparar suas consultas com seu próprio esquema e dados, em vez de confiar em meus testes.
Se é um bom design ou não depende muito da sua aplicação específica. Em termos de manutenção, não vejo nenhum problema em particular. Isso simplifica o código do seu aplicativo e significa que há menos para manter nessa parte do aplicativo. Se o PG pode fornecer exatamente o resultado que você precisa, a única razão que posso pensar para não usá-lo seriam considerações de desempenho. Não reinvente a roda e tudo.
Nulos
Funções agregadas normalmente devolvem
NULL
quando eles operam em zero linhas. Se esta for uma possibilidade, você pode querer usar COALESCE
para evitá-los. Alguns exemplos:SELECT COALESCE(json_agg(t), '[]'::json) FROM t
Ou
SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t
Crédito a Hannes Landeholm por apontar isso