eliminare i valori di array duplicati in postgres


87

Ho un array di tipo bigint, come posso rimuovere i valori duplicati in quell'array?

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

Dovrei ottenere array[1234, 5343, 6353, ...]

Ho provato l'esempio SELECT uniq(sort('{1,2,3,2,1}'::int[]))nel manuale di postgres ma non funziona.

Risposte:


94

Ho affrontato lo stesso. Ma un array nel mio caso viene creato tramite array_aggfunzione. E fortunatamente permette di aggregare valori DISTINCT , come:

  array_agg(DISTINCT value)

Questo funziona per me.


5
Notare che DISTINCT non è supportato per le funzioni della finestra.
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]) restituisce "{{1,2,2,3}}"
user48956

@ user48956, questo è logico, poiché inserisci un array come valore, devi impostare una singola colonna come valore raggruppato nella query
Daniël Tulp

83

Le funzioni sort(int[])euniq(int[]) sono fornite dal modulo contrib di intarray .

Per abilitarne l'utilizzo, è necessario installare il modulo .

Se non vuoi usare il modulo contrib intarray, o se devi rimuovere duplicati da array di tipo diverso, hai altri due modi.

Se hai almeno PostgreSQL 8.4 potresti trarre vantaggio dalla unnest(anyarray)funzione

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

In alternativa puoi creare la tua funzione per farlo

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

Ecco un esempio di invocazione:

SELECT array_sort_unique('{1,2,3,2,1}'::int[]);
 array_sort_unique 
-------------------
 {1,2,3}
(1 row)

1
La soluzione del problema ("eliminare i valori di matrice duplicati") non ha bisogno di essere ordinata . Sebbene di solito sia una funzionalità utile, non è necessaria (costo della CPU) in questo contesto / requisito.
Peter Krauss

27

... Dove sono le librerie standard (?) Per questo tipo di utilità array_X ??

Prova a cercare ... Vedi alcuni ma nessuno standard:


La array_distinct()funzione snippet-lib più semplice e veloce

Qui l'implementazione più semplice e forse più veloce per 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: funziona come previsto con qualsiasi tipo di dati, eccetto con array di array,

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

l '"effetto collaterale" è quello di far esplodere tutti gli array in un insieme di elementi.

PS: con gli array JSONB funziona bene,

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

Modifica: più complesso ma utile, un parametro "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;

puoi spiegare cosa sta facendo t (x) in FROM unest ($ 1) t (x) ... anche come posso mantenere l'ordine degli elementi in cui sono stati inseriti
abhirathore2006

@ abhirathore2006 questa risposta è un Wiki, puoi scrivere le spiegazioni che hai suggerito. Riguardo a "mantenere l'ordine", no, è una soluzione distruttiva, vedere le soluzioni PLpgSQL in questa pagina per preservare l'ordine originale degli array. E 'anche commom i due requiriments, sorta e distinti (vedi Successo di risposta principale qui e il mio commento lì).
Peter Krauss

nessun problema, ho già trovato la soluzione da qualche altra parte, sì, questa è la soluzione
plsql

13

Ho assemblato una serie di stored procedure (funzioni) per combattere la mancanza di PostgreSQL nella gestione degli array coniata anyarray. Queste funzioni sono progettate per funzionare su qualsiasi tipo di dati di array, non solo interi come fa intarray: https://www.github.com/JDBurnZ/anyarray

Nel tuo caso, tutto ciò di cui hai veramente bisogno è anyarray_uniq.sql. Copia e incolla il contenuto di quel file in una query PostgreSQL ed eseguilo per aggiungere la funzione. Se hai bisogno anche dell'ordinamento degli array, aggiungi anche anyarray_sort.sql.

Da lì, puoi eseguire una semplice query come segue:

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

Restituisce qualcosa di simile a: ARRAY[1234, 6353, 5343]

O se hai bisogno di ordinare:

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

Ritorno esattamente: ARRAY[1234, 5343, 6353]


13

L'uso DISTINCTimplicitamente ordina l'array. Se l' ordine relativo degli elementi dell'array deve essere preservato durante la rimozione dei duplicati, la funzione può essere progettata come segue: (dovrebbe funzionare dalla 9.4 in poi)

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
migliore risposta! vedi anche: dba.stackexchange.com/questions/211501/…
fjsj

9

Ecco il modo "in linea":

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;

Per prima cosa creiamo un set da array, quindi selezioniamo solo voci distinte e quindi lo aggreghiamo nuovamente in array.


9
O "più in linea" ;-) SELECT array_agg(DISTINCT c1) FROM unnest(ARRAY[1234,5343,6353,1234,1234]) t(c1)
Peter Krauss

4

In una singola query ho fatto questo:

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


3

Per le persone come me che hanno ancora a che fare con postgres 8.2, questa funzione ricorsiva può eliminare i duplicati senza alterare l'ordinamento dell'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;

per esempio :

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}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.