【问题标题】:PostgreSQL custom aggregate: more efficient option than array concatenationPostgreSQL 自定义聚合:比数组连接更有效的选项
【发布时间】:2020-07-30 20:45:29
【问题描述】:

考虑一个用于获取一组数组的集合并集的自定义聚合:

CREATE FUNCTION array_union_step (s ANYARRAY, n ANYARRAY) RETURNS ANYARRAY
   AS $$ SELECT s || n; $$
   LANGUAGE SQL IMMUTABLE LEAKPROOF PARALLEL SAFE;

CREATE FUNCTION array_union_final (s ANYARRAY) RETURNS ANYARRAY
  AS $$
    SELECT array_agg(i ORDER BY i) FROM (
      SELECT DISTINCT UNNEST(x) AS i FROM (VALUES(s)) AS v(x)
    ) AS w WHERE i IS NOT NULL;
  $$
  LANGUAGE SQL IMMUTABLE LEAKPROOF PARALLEL SAFE;

CREATE AGGREGATE array_union (ANYARRAY) (
  SFUNC = array_union_step,
  STYPE = ANYARRAY,
  FINALFUNC = array_union_final,
  INITCOND = '{}',
  PARALLEL = SAFE
);

据我了解,PostgreSQL 中的数组连接将两个输入的所有元素复制到一个新数组中,因此这是元素总数的二次方(在重复数据删除之前)。如果不用 C 语言编写扩展代码,是否有更有效的替代方案? (具体来说,使用 LANGUAGE SQL 或 LANGUAGE plpgsql。)例如,step 函数可能会以某种方式获取并返回一组行?


需要能够处理的数据类型示例:

create temp table demo (tag int, values text[]);
insert into demo values
   (1, '{"a", "b"}'),
   (2, '{"c", "d"}'),
   (1, '{"a"}'),
   (2, '{"c", "e", "f"}');

select tag, array_union(values) from demo group by tag;
 tag | array_union 
-----+-------------
   2 | {c,d,e,f}
   1 | {a,b}

请特别注意,内置的array_agg不能与这些数据一起使用,因为数组的长度不同:

select tag, array_agg(values) from demo group by tag;
ERROR:  cannot accumulate arrays of different dimensionality

【问题讨论】:

    标签: postgresql aggregate-functions plpgsql


    【解决方案1】:

    数组连接很昂贵。这就是内置array_agg() 使用内部array builder 结构的原因。很遗憾,您不能在 SQL 级别上使用此 API。

    我认为使用临时表进行自定义聚合是不正确的。创建和删除表很昂贵(临时表也是)或非常昂贵(高频),INSERTSELECT 也很昂贵。 (表的格式比数组复杂得多。)如果您需要真正快速的聚合,那么编写一个 C 函数并使用 array builder

    如果您不能使用自己的 C 扩展,则使用内置的 array_agg() 函数进行重复数据删除并对已聚合的数据进行排序。

    CREATE OR REPLACE FUNCTION array_distinct(anyarray)
    RETURNS anyarray AS $$
    BEGIN
      RETURN ARRAY(SELECT DISTINCT v FROM unnest($1) u(v) WHERE v IS NOT NULL ORDER BY v);
    END;
    $$ LANGUAGE plpgsql IMMUTABLE STRICT;
    

    呼叫:

    SELECT ..., array_distinct(array_agg(somecolumn)) FROM tab;
    

    【讨论】:

    • 或者只是LANGUAGE sql?与PARALLEL SAFE?
    • 我很好奇,只有数组连接还是数组索引分配很慢? this aggregate 也可以通过 array_builder 改进吗?
    • 不幸的是,这对我不起作用,因为array_agg(somecolumn) 仅在somecolumn 具有 scalar 类型(pg-ese 中的anyelement)或当somecolumn 中的所有数组具有完全相同的维度时。我有一列 variable 长度的一维数组。
    • @ErwinBrandstetter - 是的,也有可能,而且速度更快
    • @zwol - array_agg 也可以聚合数组。只需要 Postgres 9.5 及更高版本。但是如果不能使用array_agg,则需要编写自己的C函数。
    猜你喜欢
    • 2020-02-04
    • 1970-01-01
    • 2012-06-11
    • 2013-12-17
    • 1970-01-01
    • 1970-01-01
    • 2014-02-12
    • 2015-07-06
    • 1970-01-01
    相关资源
    最近更新 更多