如何从任何数组中删除重复项并保留 PostgreSQL 中的顺序?

Joh*_*zer 5 postgresql order-by duplication array

我正在寻找一种方法来消除 PostgreSQL 数组中的重复项,同时保留其元素的顺序。我目前拥有的是以下功能:

create function array_unique( anyarray ) 
  returns anyarray immutable strict language sql as $$
  select array( select distinct unnest( $1 ) ); $$;

create function array_unique_sorted( anyarray ) 
  returns anyarray immutable strict language sql as $$
  select array( select distinct unnest( $1 ) order by 1 ); $$;

/* ### TAINT there ought to be a simpler, declarative solution */
create function array_unique_stable( text[] )
  returns text[] immutable strict parallel safe language plpgsql as $$
  declare
    R         text[] = '{}';
    ¶element  text;
  begin
    foreach ¶element in array $1 loop
      if not array[ ¶element ] && R then
        R :=  R || array[ ¶element ];
        end if;
      end loop;
    return R; end; $$;
Run Code Online (Sandbox Code Playgroud)

在上面,array_unique接受任何类型的数组并返回一个删除所有重复项的副本;它们的相对顺序是任意的。 array_unique_sorted就像array_unique,但元素是相对于彼此排序的;这有时很有用,因为所有具有相同不同元素集的数组在被此函数归一化后将比较相等。

array_unique_stable已经做了我正在寻找的:给定一个数组(在这个例子中必须是一个text[]数组),它从左到右扫描元素;每当它遇到一个以前看不见的元素时,它就会将该元素添加到结果中。因此,只保留每个值的第一次出现。

但是,该实现有一些缺点:首先,似乎没有办法编写它,因此它接受伪类型anyarray

其次,虽然前两个函数是用 SQL 编写的,但它们可能是内联的, array_unique_stable是用 PL/pgSQL 编写的,因此不能内联。

第三,我无法在纯 SQL 中提出解决方案,这让我感到困扰……

a_h*_*ame 9

这确实可以使用纯 SQL 来完成:

create function array_unique_stable(p_input anyarray)
  returns anyarray immutable strict parallel safe 
  language sql
as 
$$
select array_agg(t order by x)
from (
  select distinct on (t) t,x
  from unnest(p_input) with ordinality as p(t,x)
  order by t,x
) t2;
$$
Run Code Online (Sandbox Code Playgroud)

unnest(p_input) with ordinality将返回其随后被用于将它们聚集回到在外部查询阵列中的元素的原始索引。

select array_unique_stable(array['a','x','x','b']) as text_array, 
       array_unique_stable(array[10,1,1,5,8,8]) as int_array
Run Code Online (Sandbox Code Playgroud)

返回

text_array | int_array 
-----------+-----------
{a,x,b}    | {10,1,5,8}
Run Code Online (Sandbox Code Playgroud)

  • 确实谢谢!我意识到在我扔掉代码并用 PL/pgSQL 重写它之前我已经非常接近了......FWIW 在 /sf/answers/2967950821/ 上有一个基本相同的答案,我后来发现了;在其他新闻中,关于 DBA 和一般 SE 的类似问题有很多不正确的答案,所以我想在这里有另一个正确的答案是件好事。 (2认同)