AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 54283
Accepted
Christoph
Christoph
Asked: 2013-12-03 12:48:12 +0800 CST2013-12-03 12:48:12 +0800 CST 2013-12-03 12:48:12 +0800 CST

Como transformar o array JSON em array Postgres?

  • 772

Eu tenho uma coluna datado tipo jsonque contém documentos JSON como este:

{
    "name": "foo",
    "tags": ["foo", "bar"]
}

Gostaria de transformar a tagsmatriz 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 jsonentrada. Então eu me pergunto como transformar esse array JSON em um array Postgres (type text[])?

postgresql postgresql-9.3
  • 7 7 respostas
  • 359134 Views

7 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2013-12-03T13:56:51+08:002013-12-03T13:56:51+08:00

    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 (type text[]) a partir do conjunto resultante de text.
    Ou string_agg()para construir uma string com uma lista de valores (tipo text).

    Focando na saída do arraytext[] ( ), não na string ( text). Diferença importante : nullos elementos são preservados em arrays reais . Isso não é possível em uma string, que não pode conter nullvalores. A verdadeira representação é uma matriz.

    Substitua 'jsonb' por 'json' para digitar jsonem todo o código SQL a seguir.

    TLDR: use uma função personalizada

    Encapsule a lógica em uma função para uso repetido:

    CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(_js jsonb)
      RETURNS text[]
      LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
    'SELECT ARRAY(SELECT jsonb_array_elements_text(_js))';
    

    Ligar:

    SELECT tbl_id, jsonb_array_to_text_array(data->'tags')
    FROM   tbl;
    
    • LANGUAGE sqlpara 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:
      • Quando marcar funções como PARALLEL RESTRICTED vs PARALLEL SAFE?
    • STRICTpara retornar nullpara nullentrada. Também: mais rápido. A função não pode ser embutida de qualquer maneira por causa do construtor ARRAY, portanto, STRICTnão pode prejudicar isso.

    Essa função com um STRICTmodificador também é o mais fiel possível ao original , pois retorna nullpara nullentrada 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 → jsonbconversão.

    Várias soluções, passo a passo

    Agregue imediatamente por linha em uma LATERALou subconsulta correlacionada, então a ordem original é preservada e não precisamos ORDER BY, GROUP BYou mesmo uma chave exclusiva na consulta externa. Ver:

    • Como aplicar ORDER BY e LIMIT em combinação com uma função agregada?
    • Qual é a diferença entre LATERAL e uma subconsulta no PostgreSQL?
    • Como aplicar ORDER BY e LIMIT em combinação com uma função agregada?
    • Por que array_agg() é mais lento que o construtor ARRAY() não agregado?

    Consulta básica, retorna nullpara matriz ou nullentrada vazia:

    SELECT t.tbl_id, d.txt_arr
    FROM   tbl t
    CROSS  JOIN LATERAL (
       SELECT array_agg(d.elem) AS txt_arr
       FROM   jsonb_array_elements_text(t.data->'tags') AS d(elem)
       ) AS d;
    

    Sintaxe curta, retorna nullpara matriz ou nullentrada vazia:

    SELECT t.tbl_id, d.txt_arr
    FROM   tbl t, LATERAL (
       SELECT array_agg(value) AS txt_arr
       FROM   jsonb_array_elements_text(t.data->'tags')  -- default name is "value"
       ) d;
    

    Mais curto (e mais rápido) com o construtor ARRAY, retorna uma matriz vazia para uma matriz ou nullentrada vazia:

    SELECT t.tbl_id, t.data->'tags' AS jsonb_arr, d.txt_arr
    FROM   tbl t, LATERAL (
       SELECT ARRAY(SELECT jsonb_array_elements_text(t.data->'tags'))
       ) d(txt_arr);
    

    Ainda mais curto (e mais rápido) com subconsulta correlacionada, retorna matriz vazia para matriz ou nullentrada vazia:

    SELECT tbl_id, ARRAY(SELECT jsonb_array_elements_text(t.data->'tags')) AS txt_arr
    FROM   tbl t;
    

    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 JOINremove linhas com matrizes ausentes ou vazias. Também pode ser útil para processar elementos. Precisamos de uma chave exclusiva para agregar:

    SELECT t.tbl_id, string_agg(d.elem::text, ', ') AS list
    FROM   tbl t
    CROSS  JOIN LATERAL json_array_elements(t.data->'tags') AS d(elem)
    GROUP  BY t.tbl_id;
    

    Construtor ARRAY, ainda com strings entre aspas:

    SELECT tbl_id, ARRAY(SELECT json_array_elements(t.data->'tags')) AS quoted_txt_arr
    FROM   tbl t;
    

    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():

    SELECT t.tbl_id, string_agg(trim(d.elem::text, '"'), ', ') AS list
    FROM   tbl t, json_array_elements(t.data->'tags') d(elem)
    GROUP  BY 1;
    

    Recupere uma única linha de tbl:

    SELECT string_agg(trim(d.elem::text, '"'), ', ') AS list
    FROM   tbl t, json_array_elements(t.data->'tags') d(elem)
    WHERE  t.tbl_id = 1;
    

    Strings formam subconsulta correlacionada:

    SELECT tbl_id, (SELECT string_agg(trim(value::text, '"'), ', ')
                    FROM   json_array_elements(t.data->'tags')) AS list
    FROM   tbl t;
    

    Construtor ARRAY:

    SELECT tbl_id, ARRAY(SELECT trim(value::text, '"')
                         FROM   json_array_elements(t.data->'tags')) AS txt_arr
    FROM   tbl t;
    

    db<>fiddle aqui
    Velho sqlfiddle

    Relacionado:

    • Precisa selecionar um elemento de matriz JSON dinamicamente de uma tabela PostgreSQL

    Notas originais (desatualizadas desde a página 9.4)

    Precisaríamos de um json_array_elements_text(json), o gêmeo de json_array_elements(json)para retornar valores apropriados textde uma matriz JSON. Mas isso parece estar faltando no arsenal fornecido de funções JSON . Ou alguma outra função para extrair um textvalor de um valor escalar json. Parece que estou sentindo falta disso também.
    Então eu improvisei com trim(), mas isso falhará para casos não triviais ...

    • 157
  2. andrew.carpenter
    2015-10-06T15:56:48+08:002015-10-06T15:56:48+08:00

    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:

    CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(p_input jsonb)
     RETURNS text[]
     LANGUAGE sql
     IMMUTABLE
    AS $function$
    
    SELECT array_agg(ary)::text[] FROM jsonb_array_elements_text(p_input) AS ary;
    
    $function$;
    

    Depois é só fazer:

    SELECT jsonb_array_to_text_array('["a", "b", "c"]'::jsonb);
    

    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á nullse 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 for null, mas ainda retorna null se a entrada for nula.

    CREATE OR REPLACE FUNCTION jsonb_array_to_text_array_strict(p_input jsonb)
     RETURNS text[]
     LANGUAGE sql
     IMMUTABLE
    AS $function$
    
    SELECT 
      CASE 
        WHEN p_input IS null 
        THEN null 
        ELSE coalesce(ary_out, ARRAY[]::text[]) 
      END
    FROM (
      SELECT array_agg(ary)::text[] AS ary_out
      FROM jsonb_array_elements_text(p_input) AS ary
    ) AS extracted;
    
    $function$
    ;
    
    • 23
  3. intgr
    2014-01-21T02:50:37+08:002014-01-21T02:50:37+08:00

    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:

    CREATE FUNCTION json_text(json) RETURNS text IMMUTABLE LANGUAGE sql
    AS $$ SELECT ('['||$1||']')::json->>0 $$;
    
    db=# select json_text(json_array_elements('["hello",1.3,"\u2603"]'));
     json_text 
    -----------
     hello
     1.3
     ☃
    

    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).

    CREATE FUNCTION json_array_elements_text(json) RETURNS SETOF text IMMUTABLE LANGUAGE sql
    AS $$ SELECT $1->>i FROM generate_series(0, json_array_length($1)-1) AS i $$;
    
    db=# select json_array_elements_text('["hello",1.3,"\u2603"]');
     json_array_elements_text 
    --------------------------
     hello
     1.3
     ☃
    
    • 8
  4. FiscalCliff
    2016-12-06T00:33:47+08:002016-12-06T00:33:47+08:00

    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.

    SELECT * 
    FROM   test 
    WHERE  TRANSLATE(jsonb::jsonb::text, '[]','{}')::INT[] 
           && ARRAY[1,2,3];
    

    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.

    SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::INT[];
    

    e se você preferir arrays text[]

    SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::TEXT[];
    
    • 8
  5. Joel B
    2017-12-29T11:44:49+08:002017-12-29T11:44:49+08:00

    Essas poucas funções, tiradas das respostas a esta pergunta , são o que estou usando e estão funcionando muito bem

    CREATE OR REPLACE FUNCTION json_array_casttext(json) RETURNS text[] AS $f$
        SELECT array_agg(x) || ARRAY[]::text[] FROM json_array_elements_text($1) t(x);
    $f$ LANGUAGE sql IMMUTABLE;
    
    CREATE OR REPLACE FUNCTION jsonb_array_casttext(jsonb) RETURNS text[] AS $f$
        SELECT array_agg(x) || ARRAY[]::text[] FROM jsonb_array_elements_text($1) t(x);
    $f$ LANGUAGE sql IMMUTABLE;
    
    CREATE OR REPLACE FUNCTION json_array_castint(json) RETURNS int[] AS $f$
        SELECT array_agg(x)::int[] || ARRAY[]::int[] FROM json_array_elements_text($1) t(x);
    $f$ LANGUAGE sql IMMUTABLE;
    
    CREATE OR REPLACE FUNCTION jsonb_array_castint(jsonb) RETURNS int[] AS $f$
        SELECT array_agg(x)::int[] || ARRAY[]::int[] FROM jsonb_array_elements_text($1) t(x);
    $f$ LANGUAGE sql IMMUTABLE;
    

    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/ jsonbsem 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.

    • 1
  6. user5480949
    2022-07-30T01:14:00+08:002022-07-30T01:14:00+08:00

    Eu gosto da solução de tradução mencionada, então aqui está uma homenagem que não interfere no conteúdo do texto:

    SELECT ('{' || RIGHT(LEFT( '[1]'::json::text ,-1),-1) || '}')::INTEGER[]
    
    • 0
  7. Jezk
    2022-10-04T12:29:31+08:002022-10-04T12:29:31+08:00

    A solução da resposta aceita:

    CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(_js jsonb)
      RETURNS text[]
      LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
    'SELECT ARRAY(SELECT jsonb_array_elements_text(_js))';
    

    é doce e sucinto, mas tem uma ressalva para mim. Quando invocado como:

    SELECT jsonb_array_to_text_array(NULL);
    

    Ele retorna NULL, conforme desejado para o meu caso de uso. No entanto, quando passado um valor nulo de uma propriedade JSON, como:

    SELECT jsonb_array_to_text_array('{"my_value_is_null": null}'::JSONB -> 'my_value_is_null');
    

    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:

    CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(_js jsonb)
      RETURNS text[]
      LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
    $$SELECT CASE WHEN _js = 'null' THEN NULL ELSE ARRAY(SELECT jsonb_array_elements_text(_js)) END$$;
    

    lida com isso também retornando NULL nesse caso, como se passasse um valor SQL NULL regular.

    • 0

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Conceder acesso a todas as tabelas para um usuário

    • 5 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve