elimine valores de array duplicados no postgres

86

Eu tenho uma matriz do tipo bigint, como posso remover os valores duplicados dessa matriz?

Ex: array[1234, 5343, 6353, 1234, 1234]

Eu deveria pegar array[1234, 5343, 6353, ...]

Testei o exemplo SELECT uniq(sort('{1,2,3,2,1}'::int[]))no manual do postgres, mas não está funcionando.

GVK
fonte

Respostas:

92

Eu enfrentei o mesmo. Mas um array no meu caso é criado via array_aggfunção. E, felizmente, permite agregar valores DISTINCT , como:

  array_agg(DISTINCT value)

Isso funciona para mim.

Mikhail Lisakov
fonte
5
Observe que DISTINCT não é compatível com funções de janela.
Pensável em
tks guytrim(string_agg(distinct to_char(z.dat_codigo,'0000000000'),'')) as dat_codigo,
Fábio Zangirolami
4
select array_agg (DISTINCT Array [1,2,2,3]) dá "{{1,2,2,3}}"
user48956
@ user48956, isso é lógico, conforme você insere uma matriz como valor, você precisa definir uma única coluna como valor agrupado por na consulta
Daniël Tulp
83

As funções sort(int[])euniq(int[]) são fornecidas pelo módulo contrib intarray .

Para habilitar seu uso, você deve instalar o módulo .

Se você não quiser usar o módulo de contribuição do intarray, ou se tiver que remover duplicatas de arrays de tipos diferentes, existem duas outras maneiras.

Se você tem pelo menos PostgreSQL 8.4 você pode tirar vantagem da unnest(anyarray)função

SELECT ARRAY(SELECT DISTINCT UNNEST('{1,2,3,2,1}'::int[]) ORDER BY 1);
 ?column? 
----------
 {1,2,3}
(1 row)

Alternativamente, você pode criar sua própria função para fazer isso

CREATE OR REPLACE FUNCTION array_sort_unique (ANYARRAY) RETURNS ANYARRAY
LANGUAGE SQL
AS $body$
  SELECT ARRAY(
    SELECT DISTINCT $1[s.i]
    FROM generate_series(array_lower($1,1), array_upper($1,1)) AS s(i)
    ORDER BY 1
  );
$body$;

Aqui está um exemplo de invocação:

SELECT array_sort_unique('{1,2,3,2,1}'::int[]);
 array_sort_unique 
-------------------
 {1,2,3}
(1 row)
mnencia
fonte
1
A solução do problema ("eliminar valores de matriz duplicados") não precisa ser classificada . Embora geralmente seja um recurso útil, é desnecessário (custo de CPU) neste contexto / requisito.
Peter Krauss
27

... Onde estão as bibliotecas padrão (?) Para este tipo de utilitário array_X ??

Tente pesquisar ... Veja alguns, mas nenhum padrão:


array_distinct()Função snippet-lib mais simples e rápida

Aqui, a implementação mais simples e talvez mais rápida para array_unique()ou array_distinct():

CREATE FUNCTION array_distinct(anyarray) RETURNS anyarray AS $f$
  SELECT array_agg(DISTINCT x) FROM unnest($1) t(x);
$f$ LANGUAGE SQL IMMUTABLE;

NOTA: funciona como esperado com qualquer tipo de dados, exceto com matriz de matrizes,

SELECT  array_distinct( array[3,3,8,2,6,6,2,3,4,1,1,6,2,2,3,99] ), 
        array_distinct( array['3','3','hello','hello','bye'] ), 
        array_distinct( array[array[3,3],array[3,3],array[3,3],array[5,6]] );
 -- "{1,2,3,4,6,8,99}",  "{3,bye,hello}",  "{3,5,6}"

o "efeito colateral" é explodir todas as matrizes em um conjunto de elementos.

PS: com arrays JSONB funciona bem,

SELECT array_distinct( array['[3,3]'::JSONB, '[3,3]'::JSONB, '[5,6]'::JSONB] );
 -- "{"[3, 3]","[5, 6]"}"

Editar: mais complexo, mas útil, um parâmetro "drop nulls"

CREATE FUNCTION array_distinct(
      anyarray, -- input array 
      boolean DEFAULT false -- flag to ignore nulls
) RETURNS anyarray AS $f$
      SELECT array_agg(DISTINCT x) 
      FROM unnest($1) t(x) 
      WHERE CASE WHEN $2 THEN x IS NOT NULL ELSE true END;
$f$ LANGUAGE SQL IMMUTABLE;
Peter Krauss
fonte
você pode explicar o que t (x) está fazendo em FROM unnest ($ 1) t (x) ... também como posso manter a ordem dos itens em que foram inseridos
abhirathore2006
@ abhirathore2006 esta resposta é um Wiki, você pode escrever as explicações que sugeriu. Sobre "manter a ordem", não, é uma solução destrutiva. Consulte as soluções PLpgSQL nesta página para preservar a ordem original do array. Também é comum os dois requisitos, sort e distinto (veja o sucesso da resposta principal aqui e meu comentário ali).
Peter Krauss
não se preocupe, eu já encontrei a solução em outro lugar, sim, essa é a solução
plsql
13

Eu montei um conjunto de stored procedures (funções) para combater a falta de manipulação de array do PostgreSQL anyarray. Essas funções são projetadas para funcionar em qualquer tipo de dados de array, não apenas inteiros como o intarray faz: https://www.github.com/JDBurnZ/anyarray

No seu caso, tudo o que você realmente precisa é anyarray_uniq.sql. Copie e cole o conteúdo desse arquivo em uma consulta PostgreSQL e execute-o para adicionar a função. Se você também precisar de classificação de array, adicione também anyarray_sort.sql.

A partir daí, você pode realizar uma consulta simples da seguinte maneira:

SELECT ANYARRAY_UNIQ(ARRAY[1234,5343,6353,1234,1234])

Retorna algo semelhante a: ARRAY[1234, 6353, 5343]

Ou se você precisar de classificação:

SELECT ANYARRAY_SORT(ANYARRAY_UNIQ(ARRAY[1234,5343,6353,1234,1234]))

Retorne exatamente: ARRAY[1234, 5343, 6353]

Joshua Burns
fonte
13

Usando DISTINCTimplicitamente classifica a matriz. Se a ordem relativa dos elementos da matriz precisa ser preservada ao remover duplicatas, a função pode ser projetada da seguinte forma: (deve funcionar a partir de 9.4)

CREATE OR REPLACE FUNCTION array_uniq_stable(anyarray) RETURNS anyarray AS
$body$
SELECT
    array_agg(distinct_value ORDER BY first_index)
FROM 
    (SELECT
        value AS distinct_value, 
        min(index) AS first_index 
    FROM 
        unnest($1) WITH ORDINALITY AS input(value, index)
    GROUP BY
        value
    ) AS unique_input
;
$body$
LANGUAGE 'sql' IMMUTABLE STRICT;
Tbussmann
fonte
1
melhor resposta! veja também: dba.stackexchange.com/questions/211501/…
fjsj
9

Esta é a maneira "embutida":

SELECT 1 AS anycolumn, (
  SELECT array_agg(c1)
  FROM (
    SELECT DISTINCT c1
    FROM (
      SELECT unnest(ARRAY[1234,5343,6353,1234,1234]) AS c1
    ) AS t1
  ) AS t2
) AS the_array;

Primeiro criamos um conjunto de array, depois selecionamos apenas entradas distintas e, em seguida, agregamos de volta ao array.

alexkovelsky
fonte
9
Ou "mais inline" ;-) SELECT array_agg(DISTINCT c1) FROM unnest(ARRAY[1234,5343,6353,1234,1234]) t(c1)
Peter Krauss
4

Em uma única consulta, fiz o seguinte:

SELECT (select array_agg(distinct val) from ( select unnest(:array_column) as val ) as u ) FROM :your_table;

Gregorio Freidin
fonte
3

Para pessoas como eu, que ainda precisam lidar com o postgres 8.2, esta função recursiva pode eliminar duplicatas sem alterar a classificação do array

CREATE OR REPLACE FUNCTION my_array_uniq(bigint[])
  RETURNS bigint[] AS
$BODY$
DECLARE
    n integer;
BEGIN

    -- number of elements in the array
    n = replace(split_part(array_dims($1),':',2),']','')::int;

    IF n > 1 THEN
        -- test if the last item belongs to the rest of the array
        IF ($1)[1:n-1] @> ($1)[n:n] THEN
            -- returns the result of the same function on the rest of the array
            return my_array_uniq($1[1:n-1]);
        ELSE
            -- returns the result of the same function on the rest of the array plus the last element               
            return my_array_uniq($1[1:n-1]) || $1[n:n];
        END IF;
    ELSE
        -- if array has only one item, returns the array
        return $1;
    END IF;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE;

por exemplo :

select my_array_uniq(array[3,3,8,2,6,6,2,3,4,1,1,6,2,2,3,99]);

darei

{3,8,2,6,4,1,99}
bayonatof
fonte