有效合并包含重叠值的数组

时间:2017-08-11 17:23:48

标签: postgresql postgresql-9.6

给定一个包含整数数组的表,应该合并数组,以便所有具有重叠条目的数组最终都是一个。

给出表arrays

     a      
------------
 {1,2,3}
 {1,4,7}
 {4,7,9}
 {15,17,18}
 {18,16,15}
 {20}

结果应如下所示

{1,2,3,4,7,9}
{15,17,18,16}
{20}

正如您所看到的,可能会删除合并数组中的重复值,并且数组中结果条目的顺序并不重要。数组是整数数组,因此可以使用intarray模块中的函数。

这将在一个非常大的表上完成,因此性能至关重要。

我的第一个天真的方法是在&&运算符上自行加入表格。像这样:

SELECT DISTINCT uniq(sort(t1.a || t2.a))
FROM arrays t1
JOIN arrays t2 ON t1.a && t2.a

这留下了两个问题:

  1. 它不是递归的(它最多合并2个数组) 这可能通过递归CTE解决。
  2. 合并的数组会在输出中重新出现。
  3. 非常欢迎任何意见。

2 个答案:

答案 0 :(得分:2)

do $$
declare
    arr int[];
    arr_id int := 0;
    tmp_id int;
begin
    create temporary table tmp (v int primary key, id int not null);
    for arr in select a from t loop
        select id into tmp_id from tmp where v = any(arr) limit 1;
        if tmp_id is NULL then
            tmp_id = arr_id;
            arr_id = arr_id+1;
        end if;
        insert into tmp
            select unnest(arr), tmp_id
            on conflict do nothing;
    end loop;
end
$$;
select array_agg(v) from tmp group by id;

答案 1 :(得分:1)

纯SQL版本:

WITH RECURSIVE x (a) AS (VALUES ('{1,2,3}'::int2[]),
 ('{1,4,7}'),
 ('{4,7,9}'),
 ('{15,17,18}'),
 ('{18,16,15}'),
 ('{20}')
), y AS (
    SELECT 1::int AS lvl,
           ARRAY [ a::text ] AS a,
           a AS res
      FROM x
     UNION ALL
    SELECT lvl + 1,
           t1.a || ARRAY [ t2.a::text ],
           (SELECT array_agg(DISTINCT unnest ORDER BY unnest)
              FROM (SELECT unnest(t1.res) UNION SELECT unnest(t2.a)) AS a) 
      FROM y AS t1
      JOIN x AS t2 ON (t2.a && t1.res) AND NOT t2.a::text = ANY(t1.a)
     WHERE lvl < 10
)
SELECT DISTINCT res
  FROM x
  JOIN LATERAL (SELECT res FROM y WHERE x.a && y.res ORDER BY lvl DESC LIMIT 1) AS z ON true