数组的有效合并(删除重复项)

Ale*_*ros 11 postgresql aggregate array postgresql-9.3

我有两张桌子,left2right2。两个表都会很大(1-10M 行)。

CREATE TABLE left2(id INTEGER, t1 INTEGER, d INTEGER);
ALTER TABLE left2 ADD PRIMARY KEY (id,t1);

CREATE TABLE right2( t1 INTEGER, d INTEGER, arr INTEGER[] );
ALTER TABLE right2 ADD PRIMARY KEY(t1,d);
Run Code Online (Sandbox Code Playgroud)

我将执行这种类型的查询:

SELECT l.d + r.d,
       UNIQ(SORT((array_agg_mult(r.arr)))
FROM left2 l,
     right2 r
WHERE l.t1 = r.t1
GROUP BY l.d + r.d
ORDER BY l.d + r.d;
Run Code Online (Sandbox Code Playgroud)

在哪里聚合数组我使用函数:

CREATE AGGREGATE array_agg_mult(anyarray) (
SFUNC=array_cat,
STYPE=anyarray,
INITCOND='{}');
Run Code Online (Sandbox Code Playgroud)

连接数组后,我使用模块的UNIQ功能intarray。有没有更有效的方法来做到这一点?该arr字段上是否有任何索引可以加速合并(删除重复项)?聚合函数可以直接去除重复吗?如果有帮助,可以将原始数组视为已排序(并且它们是唯一的)。

SQL小提琴在这里

Erw*_*ter 10

结果正确吗?

首先:正确性。您想生成一组唯一元素吗?您当前的查询没有这样做。该功能uniq()intarray模块只承诺:

删除相邻的重复项

就像手册中的说明一样,您需要:

SELECT l.d + r.d, uniq(sort(array_agg_mult(r.arr)))
FROM   ...
Run Code Online (Sandbox Code Playgroud)

还给你排序的数组 - 假设你想要,你没有澄清。

我看你 sort()你的小提琴,所以这可能只是你的问题中一个错字。

Postgres 9.5 或更高版本

无论哪种方式,由于 Postgres 9.5array_agg()具有我array_agg_mult()内置的开箱即用功能,而且速度也更快:

阵列处理方面也有其他性能改进。

询问

的主要目的array_agg_mult()是聚合多维数组,但无论如何你只能生成一维数组。所以我至少会尝试这个替代查询:

SELECT l.d + r.d AS d_sum, array_agg(DISTINCT elem) AS result_arr
FROM   left2  l
JOIN   right2 r USING (t1)
     , unnest(r.arr) elem
GROUP  BY 1
ORDER  BY 1;
Run Code Online (Sandbox Code Playgroud)

这也解决了您的问题:

聚合函数可以直接去除重复吗?

是的,它可以,与DISTINCT。但这并不比uniq()整数数组快,整数数组已经针对整数数组进行了优化,而DISTINCT对于所有符合条件的数据类型都是通用的。

不需要intarray模块。但是,结果不一定要排序。Postgres 对DISTINCT. 大集通常是散列的,除非您添加显式,否则结果将保持未排序ORDER BY。如果需要排序数组,可以ORDER BY直接添加到聚合函数中:

array_agg(DISTINCT elem ORDER BY elem)
Run Code Online (Sandbox Code Playgroud)

但这通常比将预先排序的数据提供给(一个大排序对许多小排序)要慢array_agg()。所以我会在子查询中排序,然后聚合:

SELECT d_sum, uniq(array_agg(elem)) AS result_arr
FROM  (
   SELECT l.d + r.d AS d_sum, elem
   FROM   left2  l
   JOIN   right2 r USING (t1)
        , unnest(r.arr) elem
   ORDER  BY 1, 2
   ) sub
GROUP  BY 1
ORDER  BY 1;
Run Code Online (Sandbox Code Playgroud)

这是我对 Postgres 9.4 的粗略测试中最快的变体。

SQL Fiddle基于您提供的那个。

指数

我认为这里没有任何索引的潜力。唯一的选择是:

CREATE INDEX ON right2 (t1, arr);
Run Code Online (Sandbox Code Playgroud)

仅当您从中获得仅索引扫描时才有意义 - 如果基础表right2比这两个列宽得多并且您的设置符合仅索引扫描的条件,则会发生这种情况。Postgres Wiki 中的详细信息。