Eu tenho uma coluna data
do tipo json
que contém documentos JSON como este:
{
"name": "foo",
"tags": ["foo", "bar"]
}
Gostaria de transformar a tags
matriz aninhada em uma string concatenada ( 'foo, bar'
). Isso seria facilmente possível com a array_to_string()
função em teoria. No entanto, esta função não aceita json
entrada. Então eu me pergunto como transformar esse array JSON em um array Postgres (type text[]
)?
Postgres 9.4 ou mais recente
Inspirado por este post , o Postgres 9.4 adicionou as funções ausentes para desaninhar arrays JSON.
Obrigado a Laurence Rowe pelo patch e Andrew Dunstan por se comprometer!
json_array_elements_text(json)
jsonb_array_elements_text(jsonb)
Use
array_agg()
ou um construtor ARRAY para construir um array Postgres (typetext[]
) a partir do conjunto resultante detext
.Ou
string_agg()
para construir uma string com uma lista de valores (tipotext
).Focando na saída do array
text[]
( ), não na string (text
). Diferença importante :null
os elementos são preservados em arrays reais . Isso não é possível em uma string, que não pode conternull
valores. A verdadeira representação é uma matriz.Substitua 'jsonb' por 'json' para digitar
json
em todo o código SQL a seguir.TLDR: use uma função personalizada
Encapsule a lógica em uma função para uso repetido:
Ligar:
LANGUAGE sql
para a função simples. (Mais rápido em meus últimos testes com o Postgres 14.)IMMUTABLE
(porque é) para evitar avaliações repetidas em consultas maiores e permitir seu uso em expressões de índice.PARALLEL SAFE
(no Postgres 9.6 ou posterior!) para permitir a execução paralela em grandes consultas. Ver:STRICT
para retornarnull
paranull
entrada. Também: mais rápido. A função não pode ser embutida de qualquer maneira por causa do construtor ARRAY, portanto,STRICT
não pode prejudicar isso.Essa função com um
STRICT
modificador também é o mais fiel possível ao original , pois retornanull
paranull
entrada e uma matriz vazia para entrada de matriz vazia. Melhor do que todas as consultas abaixo.Para completar: use
to_jsonb()
para o array SQL reverso →jsonb
conversão.Várias soluções, passo a passo
Agregue imediatamente por linha em uma
LATERAL
ou subconsulta correlacionada, então a ordem original é preservada e não precisamosORDER BY
,GROUP BY
ou mesmo uma chave exclusiva na consulta externa. Ver:Consulta básica, retorna
null
para matriz ounull
entrada vazia:Sintaxe curta, retorna
null
para matriz ounull
entrada vazia:Mais curto (e mais rápido) com o construtor ARRAY, retorna uma matriz vazia para uma matriz ou
null
entrada vazia:Ainda mais curto (e mais rápido) com subconsulta correlacionada, retorna matriz vazia para matriz ou
null
entrada vazia:db<>fique aqui
Todos os itens acima preservam a ordem original dos elementos.
Postgres 9.3 ou anterior
Use a função
json_array_elements()
. Mas temos strings com aspas duplas dele.Consulta alternativa com agregação na consulta externa.
CROSS JOIN
remove linhas com matrizes ausentes ou vazias. Também pode ser útil para processar elementos. Precisamos de uma chave exclusiva para agregar:Construtor ARRAY, ainda com strings entre aspas:
Observe que
null
é convertido para o valor de texto "null", diferentemente do anterior. Incorreto, estritamente falando, e potencialmente ambíguo.Pobre homem está sem aspas com
trim()
:Recupere uma única linha de tbl:
Strings formam subconsulta correlacionada:
Construtor ARRAY:
db<>fiddle aqui
Velho sqlfiddle
Relacionado:
Notas originais (desatualizadas desde a página 9.4)
Precisaríamos de um
json_array_elements_text(json)
, o gêmeo dejson_array_elements(json)
para retornar valores apropriadostext
de uma matriz JSON. Mas isso parece estar faltando no arsenal fornecido de funções JSON . Ou alguma outra função para extrair umtext
valor de um valor escalarjson
. Parece que estou sentindo falta disso também.Então eu improvisei com
trim()
, mas isso falhará para casos não triviais ...PG 9.4+
A resposta aceita é definitivamente o que você precisa, mas por uma questão de simplicidade, aqui está um auxiliar que uso para isso:
Depois é só fazer:
Atualizado em 23/02/2020 em resposta aos comentários : Os comentários estão corretos de que isso poderia ser mais eficiente. Na época em que postei, não havia solução modularizada oferecida, então ofereci uma a sério, se não fosse a ideal. Desde então, Erwin atualizou sua resposta com uma função simples e eficiente, então nunca atualizei a minha. Atualizando agora, pois ainda há atenção para esta resposta
Mais uma atualização, porque isso me mordeu : A função acima retornará
null
se não houver valores. Isso pode não ser desejável dependendo da sua situação. Aqui está uma função que retorna uma matriz vazia se o valor não fornull
, mas ainda retorna null se a entrada for nula.Essa pergunta foi feita nas listas de discussão do PostgreSQL e eu criei essa maneira hackeada de converter texto JSON para o tipo de texto PostgreSQL por meio do operador de extração de campo JSON:
Basicamente, ele converte o valor em uma matriz de elemento único e, em seguida, solicita o primeiro elemento.
Outra abordagem seria usar esse operador para extrair todos os campos um por um. Mas para matrizes grandes, isso provavelmente é mais lento, pois precisa analisar toda a string JSON para cada elemento da matriz, levando à complexidade O(n^2).
Já testei algumas opções. Aqui está a minha consulta favorita. Suponha que temos uma tabela contendo o campo id e json. O campo json contém array, que queremos transformar em array pg.
Está funcionando em qualquer lugar e mais rápido do que outros, mas parece maluco)
Primeiramente, a matriz json é convertida como texto e, em seguida, apenas alteramos os colchetes para parênteses. Finalmente, o texto está sendo convertido como array do tipo necessário.
e se você preferir arrays text[]
Essas poucas funções, tiradas das respostas a esta pergunta , são o que estou usando e estão funcionando muito bem
Em cada um deles, concatenando com um array vazio, eles lidam com um caso que me fez quebrar a cabeça um pouco, em que se você tentar lançar um array vazio de
json
/jsonb
sem ele, você não obterá nada retornado, em vez de um array vazio ({}
) como seria de esperar. Tenho certeza de que há alguma otimização para eles, mas eles são deixados como estão para simplificar a explicação do conceito.Eu gosto da solução de tradução mencionada, então aqui está uma homenagem que não interfere no conteúdo do texto:
A solução da resposta aceita:
é doce e sucinto, mas tem uma ressalva para mim. Quando invocado como:
Ele retorna NULL, conforme desejado para o meu caso de uso. No entanto, quando passado um valor nulo de uma propriedade JSON, como:
Ele lança um erro informando "não é possível extrair elementos de um escalar".
Talvez eu esteja perdendo uma parte vital da compreensão do manuseio do valor NULL, mas rastreei o problema de volta à função personalizada, onde a propriedade com valor nulo é passada como uma picada de valor 'null'. Então minha versão modificada:
lida com isso também retornando NULL nesse caso, como se passasse um valor SQL NULL regular.