Beseitigen Sie doppelte Array-Werte in Postgres

86

Ich habe ein Array vom Typ bigint. Wie kann ich die doppelten Werte in diesem Array entfernen?

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

Ich sollte bekommen array[1234, 5343, 6353, ...]

Ich habe das Beispiel SELECT uniq(sort('{1,2,3,2,1}'::int[]))im Postgres-Handbuch getestet, aber es funktioniert nicht.

GVK
quelle

Antworten:

92

Ich stand vor dem gleichen. In meinem Fall wird jedoch ein Array über eine array_aggFunktion erstellt. Und zum Glück können DISTINCT- Werte wie folgt aggregiert werden:

  array_agg(DISTINCT value)

Das funktioniert bei mir.

Mikhail Lisakov
quelle
5
Beachten Sie, dass DISTINCT für Fensterfunktionen nicht unterstützt wird.
Denkbar
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]) gibt "{{1,2,2,3}}"
user48956
@ user48956, das ist logisch, wenn Sie ein Array als Wert eingeben, müssen Sie eine einzelne Spalte als Wert festlegen, nach der in der Abfrage gruppiert wird
Daniël Tulp
83

Die Funktionen sort(int[])unduniq(int[]) werden vom Intarray Contrib- Modul bereitgestellt .

Um die Verwendung zu aktivieren, müssen Sie das Modul installieren .

Wenn Sie das Intarray-Contrib-Modul nicht verwenden möchten oder Duplikate aus Arrays unterschiedlichen Typs entfernen müssen, haben Sie zwei andere Möglichkeiten.

Wenn Sie mindestens PostgreSQL 8.4 haben, können Sie die unnest(anyarray)Funktion nutzen

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

Alternativ können Sie dazu eine eigene Funktion erstellen

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

Hier ist ein Beispielaufruf:

SELECT array_sort_unique('{1,2,3,2,1}'::int[]);
 array_sort_unique 
-------------------
 {1,2,3}
(1 row)
mnencia
quelle
1
Die Lösung des Problems ("doppelte Array-Werte entfernen") muss nicht sortiert werden . Obwohl dies normalerweise eine nützliche Funktion ist, ist es in diesem Kontext / dieser Anforderung nicht erforderlich (CPU-Kosten).
Peter Krauss
27

... wo die statandard Bibliotheken (?) Für diese Art von array_X Dienstprogramm ?

Versuchen Sie zu suchen ... Sehen Sie einige, aber keinen Standard:


Einfachste und schnellere array_distinct()Snippet-Lib-Funktion

Hier die einfachste und vielleicht schnellere Implementierung für array_unique()oder 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;

HINWEIS: Es funktioniert wie erwartet mit jedem Datentyp, außer mit Arrays von Arrays.

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

Der "Nebeneffekt" besteht darin, alle Arrays in einer Reihe von Elementen zu explodieren.

PS: mit JSONB-Arrays funktioniert gut,

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

Bearbeiten: komplexer, aber nützlich, ein "drop nulls" -Parameter

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
quelle
Kannst du bitte erklären, was t (x) in FROM unnest ($ 1) t (x) macht ... auch wie kann ich die Reihenfolge der Elemente
beibehalten
@ abhirathore2006 Diese Antwort ist ein Wiki. Sie können die von Ihnen vorgeschlagenen Erklärungen schreiben. Über "Keep the Order", nein, es ist eine destruktive Lösung. Siehe PLpgSQL-Lösungen auf dieser Seite, um die ursprüngliche Array-Reihenfolge beizubehalten. Es wird auch auf die beiden Anforderungen eingegangen, sortieren und unterscheiden (siehe Erfolg der Hauptantwort hier und meinen Kommentar dort).
Peter Krauss
Keine Sorge, ich habe die Lösung bereits von woanders gefunden, ja, das ist plsql-Lösung
abhirathore2006
13

Ich habe eine Reihe gespeicherter Prozeduren (Funktionen) zusammengestellt, um den Mangel an geprägtem Array-Handling von PostgreSQL zu bekämpfen anyarray. Diese Funktionen funktionieren für jeden Array-Datentyp, nicht nur für Ganzzahlen wie bei Intarray: https://www.github.com/JDBurnZ/anyarray

In Ihrem Fall ist alles, was Sie wirklich brauchen würden anyarray_uniq.sql. Kopieren Sie den Inhalt dieser Datei, fügen Sie ihn in eine PostgreSQL-Abfrage ein und führen Sie ihn aus, um die Funktion hinzuzufügen. Wenn Sie auch eine Array-Sortierung benötigen, fügen Sie ebenfalls hinzu anyarray_sort.sql.

Von dort aus können Sie eine einfache Abfrage wie folgt durchführen:

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

Gibt etwas Ähnliches zurück wie: ARRAY[1234, 6353, 5343]

Oder wenn Sie sortieren müssen:

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

Rückgabe genau: ARRAY[1234, 5343, 6353]

Joshua Burns
quelle
13

Mit wird DISTINCTdas Array implizit sortiert. Wenn die relative Reihenfolge der Array-Elemente beim Entfernen von Duplikaten beibehalten werden muss, kann die Funktion wie folgt gestaltet werden: (sollte ab 9.4 funktionieren)

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
quelle
1
beste Antwort! siehe auch: dba.stackexchange.com/questions/211501/…
fjsj
9

Hier ist der "Inline" Weg:

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;

Zuerst erstellen wir eine Menge aus einem Array, dann wählen wir nur bestimmte Einträge aus und aggregieren sie dann wieder zu einem Array.

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

In einer einzigen Abfrage habe ich Folgendes getan:

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

Gregorio Freidin
quelle
3

Für Leute wie mich, die sich noch mit Postgres 8.2 beschäftigen müssen, kann diese rekursive Funktion Duplikate beseitigen, ohne die Sortierung des Arrays zu ändern

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;

zum Beispiel :

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

wird geben

{3,8,2,6,4,1,99}
Bajonatof
quelle