удалить повторяющиеся значения массива в postgres

86

У меня есть массив типа bigint, как я могу удалить повторяющиеся значения в этом массиве?

Пример: array[1234, 5343, 6353, 1234, 1234]

Я должен получить array[1234, 5343, 6353, ...]

Я протестировал пример SELECT uniq(sort('{1,2,3,2,1}'::int[]))в руководстве по postgres, но он не работает.

ГВК
источник

Ответы:

92

Я столкнулся с таким же. Но в моем случае массив создается через array_aggфункцию. И, к счастью, он позволяет агрегировать DISTINCT значения, например:

  array_agg(DISTINCT value)

У меня это работает.

Михаил Лисаков
источник
5
Обратите внимание, что DISTINCT не поддерживается для оконных функций.
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]) дает "{{1,2,2,3}}"
user48956
@ user48956, это логично, поскольку вы вводите массив в качестве значения, вам нужно установить один столбец в качестве значения, которое сгруппировано в запросе,
Даниэль Тулп,
83

sort(int[])И uniq(int[])функции обеспечиваются INTArray модулем вно.

Чтобы включить его использование, необходимо установить модуль .

Если вы не хотите использовать модуль intarray contrib или если вам нужно удалить дубликаты из массивов другого типа, у вас есть два других способа.

Если у вас есть хотя бы PostgreSQL 8.4, вы можете воспользоваться unnest(anyarray)функцией

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

В качестве альтернативы вы можете создать свою собственную функцию для этого

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

Вот пример вызова:

SELECT array_sort_unique('{1,2,3,2,1}'::int[]);
 array_sort_unique 
-------------------
 {1,2,3}
(1 row)
mnencia
источник
1
Решение проблемы («устранение повторяющихся значений массива») в сортировке не требуется . Хотя обычно это полезная функция, в этом контексте / требовании она не нужна (стоимость ЦП).
Питер Краусс
27

... Где стандартные библиотеки (?) Для такого рода утилит array_X ??

Попробуйте поискать ... Посмотрите некоторые, но не стандартные:

  • postgres.cz/wiki/Array_based_functions : хорошая ссылка!

  • JDBurnZ / postgresql-anyarray , хорошая инициатива, но требует некоторого сотрудничества для улучшения.

  • wiki.postgresql.org/Snippets , неудовлетворенная инициатива, но «официальная вики», требует некоторого сотрудничества для улучшения.

  • MADlib : хорошо! .... но это слон, а не "чистая библиотека фрагментов SQL".


Самая простая и быстрая array_distinct()функция snippet-lib

Вот самая простая и, возможно, более быстрая реализация для array_unique()или 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;

ПРИМЕЧАНИЕ: он работает должным образом с любым типом данных, кроме массива массивов,

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

«побочный эффект» - разбить все массивы на набор элементов.

PS: с массивами JSONB работает нормально,

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

Изменить: более сложный, но полезный параметр "отбрасывать нули"

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;
Питер Краусс
источник
не могли бы вы объяснить, что делает t (x) в FROM unnest ($ 1) t (x) ... также как я могу сохранить порядок элементов, в которые они вставлены
abhirathore2006,
@ abhirathore2006, этот ответ - Wiki, вы можете написать объяснения, которые вы предложили. Насчет «соблюдения порядка» нет, это деструктивное решение. См. Решения PLpgSQL на этой странице, чтобы сохранить исходный порядок массива. Это также объединяет два требования, сортировку и различие (см. Успех основного ответа здесь и мой комментарий там).
Питер Краусс
не беспокойтесь, я уже нашел решение где-то еще, да, это решение
plsql
13

Я собрал набор хранимых процедур (функций) для борьбы с отсутствием в PostgreSQL придуманной обработки массивов anyarray. Эти функции предназначены для работы с любым типом данных массива, а не только с целыми числами, как это делает intarray: https://www.github.com/JDBurnZ/anyarray

В вашем случае все, что вам действительно нужно, это anyarray_uniq.sql. Скопируйте и вставьте содержимое этого файла в запрос PostgreSQL и выполните его, чтобы добавить функцию. Если вам также нужна сортировка массивов, также добавьте anyarray_sort.sql.

Оттуда вы можете выполнить простой запрос следующим образом:

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

Возвращает что-то похожее на: ARRAY[1234, 6353, 5343]

Или, если вам нужна сортировка:

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

Вернуть точно: ARRAY[1234, 5343, 6353]

Джошуа Бернс
источник
13

Использование DISTINCTнеявно сортирует массив. Если относительный порядок элементов массива необходимо сохранить при удалении дубликатов, функцию можно разработать следующим образом: (должна работать с 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
источник
1
лучший ответ! см. также: dba.stackexchange.com/questions/211501/…
fjsj
9

Вот «встроенный» способ:

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;

Сначала мы создаем набор из массива, затем выбираем только отдельные записи, а затем объединяем их обратно в массив.

Алексковельский
источник
9
Или «более инлайн» ;-) SELECT array_agg(DISTINCT c1) FROM unnest(ARRAY[1234,5343,6353,1234,1234]) t(c1)
Питер Краусс
4

В одном запросе я сделал это:

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

Грегорио Фрейдин
источник
3

Для людей вроде меня, которым все еще приходится иметь дело с postgres 8.2, эта рекурсивная функция может удалять дубликаты без изменения сортировки массива.

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;

например :

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

дам

{3,8,2,6,4,1,99}
байонат
источник