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.

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.


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)

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;

¿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]


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;

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.


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;


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}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.