eliminar valores de matriz duplicados en postgres

86

Tengo una matriz de tipo bigint, ¿cómo puedo eliminar los valores duplicados en esa matriz?

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

Debería conseguir array[1234, 5343, 6353, ...]

Probé el ejemplo SELECT uniq(sort('{1,2,3,2,1}'::int[]))en el manual de postgres pero no funciona.

GVK
fuente

Respuestas:

92

Me enfrenté a lo mismo. Pero una matriz en mi caso se crea a través de la array_aggfunción. Y afortunadamente permite agregar valores DISTINCT , como:

  array_agg(DISTINCT value)

Esto funciona para mi.

Mikhail Lisakov
fuente
5
Tenga en cuenta que DISTINCT no es compatible con las funciones de ventana.
Thinkable
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]) da "{{1,2,2,3}}"
user48956
@ user48956, eso es lógico, cuando ingresa una matriz como valor, debe establecer una sola columna como valor que se agrupa en la consulta
Daniël Tulp
83

Las funciones sort(int[])yuniq(int[]) las proporciona el módulo contrib de intarray .

Para habilitar su uso, debe instalar el módulo .

Si no desea utilizar el módulo contrib de intarray, o si tiene que eliminar duplicados de matrices de diferente tipo, tiene otras dos formas.

Si tiene al menos PostgreSQL 8.4, podría aprovechar la unnest(anyarray)función

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

Alternativamente, puede crear su propia función para hacer esto

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$;

Aquí hay una invocación de muestra:

SELECT array_sort_unique('{1,2,3,2,1}'::int[]);
 array_sort_unique 
-------------------
 {1,2,3}
(1 row)
mnencia
fuente
1
La solución del problema ("eliminar valores de matriz duplicados") no necesita ordenar . Aunque suele ser una característica útil, no es necesaria (costo de CPU) en este contexto / requisito.
Peter Krauss
27

... ¿Dónde están las bibliotecas estándar (?) Para este tipo de utilidad array_X ??

Intente buscar ... Ver algunos, pero no estándar:


La array_distinct()función snippet-lib más simple y rápida

Aquí la implementación más simple y quizás más rápida para array_unique()o 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 se esperaba con cualquier tipo de datos, excepto con una matriz de matrices,

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}"

el "efecto secundario" es descomponer todas las matrices en un conjunto de elementos.

PD: con matrices JSONB funciona bien,

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

Editar: más complejo pero útil, un parámetro "eliminar nulos"

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
fuente
¿Puede explicar qué está haciendo t (x) en FROM unnest ($ 1) t (x) ... también cómo puedo mantener el orden de los elementos en los que se insertaron
abhirathore2006
@ abhirathore2006 esta respuesta es una Wiki, puede escribir las explicaciones que sugirió. Acerca de "mantener el orden", no, es una solución destructiva. Consulte las soluciones PLpgSQL en esta página para preservar el orden original de la matriz. También se Commom los dos requiriments, tipo y distinto (véase el éxito de la respuesta principal aquí y mi comentario allí).
Peter Krauss
no se preocupe, ya encontré la solución en otro lugar, sí, esa es la solución
plsql
13

He reunido un conjunto de procedimientos almacenados (funciones) para combatir la falta de manejo de matrices de PostgreSQL anyarray. Estas funciones están diseñadas para funcionar en cualquier tipo de datos de matriz, no solo con números enteros como lo hace intarray: https://www.github.com/JDBurnZ/anyarray

En su caso, todo lo que realmente necesita es anyarray_uniq.sql. Copie y pegue el contenido de ese archivo en una consulta de PostgreSQL y ejecútelo para agregar la función. Si también necesita ordenar matrices, agregue también anyarray_sort.sql.

A partir de ahí, puede realizar una consulta simple de la siguiente manera:

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

Devuelve algo similar a: ARRAY[1234, 6353, 5343]

O si necesita ordenar:

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

Devolver exactamente: ARRAY[1234, 5343, 6353]

Joshua Burns
fuente
13

El uso DISTINCTordena implícitamente la matriz. Si es necesario conservar el orden relativo de los elementos de la matriz mientras se eliminan los duplicados, la función se puede diseñar de la siguiente manera: (debería funcionar desde 9.4 en adelante)

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
fuente
1
¡la mejor respuesta! ver también: dba.stackexchange.com/questions/211501/…
fjsj
9

Esta es la forma "en línea":

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;

Primero creamos un conjunto a partir de una matriz, luego seleccionamos solo entradas distintas y luego lo agregamos nuevamente a la matriz.

alexkovelsky
fuente
9
O "más en línea" ;-) SELECT array_agg(DISTINCT c1) FROM unnest(ARRAY[1234,5343,6353,1234,1234]) t(c1)
Peter Krauss
4

En una sola consulta hice esto:

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

Gregorio Freidin
fuente
3

Para personas como yo que todavía tienen que lidiar con postgres 8.2, esta función recursiva puede eliminar duplicados sin alterar la clasificación de la matriz

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 ejemplo :

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

daré

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