Как сравнить два массива и выбрать только несоответствующие элементы В postgres - PullRequest
25 голосов
/ 30 июня 2011

Как выбрать только несовпадающие элементы между двумя массивами.

Пример:

base_array [12,3,5,7,8]
temp_array [3,7,8]

Итак, здесь я хочу сравнить оба массива и удалить соответствующие элементы из базового массива.

Теперь base_array должен быть похож на [12,5]

Ответы [ 7 ]

27 голосов
/ 30 июня 2011

Я бы подошел к этому с оператором массива.

select array(select unnest(:arr1) except select unnest(:arr2));

Если: arr1 и: arr2 не пересекаются, использование array_agg () приводит к нулю.

21 голосов
/ 30 июня 2011
select array_agg(elements)
from (
  select unnest(array[12,3,5,7,8])
  except
  select unnest(array[3,7,8])
) t (elements)
8 голосов
/ 16 сентября 2013

Я создал набор функций, специально предназначенных для решения следующих проблем: https://github.com/JDBurnZ/anyarray

Самое замечательное, что эти функции работают со всеми типами данных, а не с целыми числами JUST, поскольку intarray ограничено.

После загрузки загрузки функций, определенных в этих файлах SQL из GitHub, все, что вам нужно сделать, это:

SELECT
  ANYARRAY_DIFF(
    ARRAY[12, 3, 5, 7, 8],
    ARRAY[3, 7, 8]
  )

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

Если вам также нужно вернуть отсортированные значения:

SELECT
  ANYARRAY_SORT(
    ANYARRAY_DIFF(
      ARRAY[12, 3, 5, 7, 8],
      ARRAY[3, 7, 8]
    )
  )

Возвращает точно: ARRAY[5, 12]

8 голосов
/ 30 июня 2011

Давайте попробуем unnest () / кроме:

EXPLAIN ANALYZE SELECT array(select unnest(ARRAY[1,2,3,n]) EXCEPT SELECT unnest(ARRAY[2,3,4,n])) FROM generate_series( 1,10000 ) n;
 Function Scan on generate_series n  (cost=0.00..62.50 rows=1000 width=4) (actual time=1.373..140.969 rows=10000 loops=1)
   SubPlan 1
     ->  HashSetOp Except  (cost=0.00..0.05 rows=1 width=0) (actual time=0.011..0.011 rows=1 loops=10000)
           ->  Append  (cost=0.00..0.04 rows=2 width=0) (actual time=0.002..0.008 rows=8 loops=10000)
                 ->  Subquery Scan "*SELECT* 1"  (cost=0.00..0.02 rows=1 width=0) (actual time=0.002..0.003 rows=4 loops=10000)
                       ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=4 loops=10000)
                 ->  Subquery Scan "*SELECT* 2"  (cost=0.00..0.02 rows=1 width=0) (actual time=0.001..0.003 rows=4 loops=10000)
                       ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=4 loops=10000)
 Total runtime: 142.531 ms

и специального оператора intarray:

EXPLAIN ANALYZE SELECT ARRAY[1,2,3,n] - ARRAY[2,3,4,n] FROM generate_series( 1,10000 ) n;
 Function Scan on generate_series n  (cost=0.00..15.00 rows=1000 width=4) (actual time=1.338..11.381 rows=10000 loops=1)
 Total runtime: 12.306 ms

Базовая линия:

EXPLAIN ANALYZE SELECT ARRAY[1,2,3,n], ARRAY[2,3,4,n] FROM generate_series( 1,10000 ) n;
 Function Scan on generate_series n  (cost=0.00..12.50 rows=1000 width=4) (actual time=1.357..7.139 rows=10000 loops=1)
 Total runtime: 8.071 ms

Время на массивпересечение:

intarray -           :  0.4 µs
unnest() / intersect : 13.4 µs

Конечно, способ intarray намного быстрее, но я нахожу удивительным, что postgres может запилить зависимый подзапрос (который содержит хэш и другие вещи) за 13,4 мкс ...

1 голос
/ 30 июня 2011

Модуль contrib / intarray предоставляет эту функцию - в любом случае для массивов целых чисел.Для других типов данных вам, возможно, придется написать свои собственные функции (или изменить те, которые предоставляются с intarray).

0 голосов
/ 11 марта 2019

Расширение ответа Дениса, которое возвращает разницу, независимо от того, какой массив был введен первым. Это не самый лаконичный запрос, возможно, у кого-то есть более аккуратный способ.

select array_cat(
   (select array(select unnest(a.b::int[]) except select unnest(a.c::int[]))),
   (select array(select unnest(a.c::int[]) except select unnest(a.b::int[]))))
from (select '{1,2}'::int[] b,'{1,3}'::int[] c) as a;

Возвращает:

{2,3}
0 голосов
/ 09 декабря 2015

Я бы создал функцию, используя ту же логику, что и описанная в @a_horse_with_no_name:

CREATE FUNCTION array_subtract(a1 int[], a2 int[]) RETURNS int[] AS $$
DECLARE
    ret int[];
BEGIN
    IF a1 is null OR a2 is null THEN
        return a1;
    END IF;
    SELECT array_agg(e) INTO ret
    FROM (
        SELECT unnest(a1)
        EXCEPT
        SELECT unnest(a2)
    ) AS dt(e);
    RETURN ret;
END;
$$ language plpgsql;

Затем вы можете использовать эту функцию для соответствующего изменения вашей переменной base_array:

base_array := array_subtract(base_array, temp_array);

Используя более быстрое решение @ Denis и только SQL, мы можем выразить обобщенную функцию как

CREATE FUNCTION array_subtract(anyarray,anyarray) RETURNS anyarray AS $f$
  SELECT array(
    SELECT unnest($1)
    EXCEPT
    SELECT unnest($2)
  )
$f$ language SQL IMMUTABLE;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...