PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

O PostgreSQL retorna o conjunto de resultados como array JSON?

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