Самое близкое к пересечению массивов, о котором я могу думать, это:
select array_agg(e)
from (
select unnest(a1)
intersect
select unnest(a2)
) as dt(e)
Это предполагает, что a1
и a2
являются одномерными массивами с элементами одного типа. Вы можете обернуть это в функцию примерно так:
create function array_intersect(a1 int[], a2 int[]) returns int[] as $$
declare
ret int[];
begin
-- The reason for the kludgy NULL handling comes later.
if a1 is null then
return a2;
elseif a2 is null then
return a1;
end if;
select array_agg(e) into ret
from (
select unnest(a1)
intersect
select unnest(a2)
) as dt(e);
return ret;
end;
$$ language plpgsql;
Затем вы можете делать такие вещи:
=> select array_intersect(ARRAY[2,4,6,8,10], ARRAY[1,2,3,4,5,6,7,8,9,10]);
array_intersect
-----------------
{6,2,4,10,8}
(1 row)
Обратите внимание, что это не гарантирует какого-либо определенного порядка в возвращаемом массиве, но вы можете исправить это, если вам это небезразлично. Затем вы можете создать свою собственную агрегатную функцию:
-- Pre-9.1
create aggregate array_intersect_agg(
sfunc = array_intersect,
basetype = int[],
stype = int[],
initcond = NULL
);
-- 9.1+ (AFAIK, I don't have 9.1 handy at the moment
-- see the comments below.
create aggregate array_intersect_agg(int[]) (
sfunc = array_intersect,
stype = int[]
);
И теперь мы видим, почему array_intersect
делает забавные и несколько неуклюжие вещи с NULL. Нам нужно начальное значение для агрегации, которое ведет себя как универсальный набор, и мы можем использовать для этого NULL (да, это немного пахнет, но я не могу придумать ничего лучше).
Как только все это будет на месте, вы можете сделать следующее:
> select * from stuff;
a
---------
{1,2,3}
{1,2,3}
{3,4,5}
(3 rows)
> select array_intersect_agg(a) from stuff;
array_intersect_agg
---------------------
{3}
(1 row)
Не совсем простой или эффективный, но, возможно, разумная отправная точка и лучше, чем вообще ничего.
Полезные ссылки:
person
mu is too short
schedule
11.08.2011