Postgresql

如何從任何數組中刪除重複項並在 PostgreSQL 中保留順序?

  • July 6, 2018

我正在尋找一種方法來消除 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; $$;

在上面,array_unique接受任何類型的數組並返回一個刪除所有重複項的副本;它們的相對順序是任意的。 array_unique_sorted是 like array_unique,但元素是相對於彼此排序的;這有時很有用,因為具有相同一組不同元素的所有數組在被此函式標準化後將比較相等。

array_unique_stable已經做了我正在尋找的:給定一個數組(在這個例子中必須是一個text[]數組),它從左到右掃描元素;每當遇到以前看不見的元素時,它就會將該元素添加到結果中。因此,僅保留每個值的第一次出現。

但是,該實現有一些缺點:首先,似乎沒有辦法編寫它,因此它接受了偽類型anyarray

其次,雖然前兩個函式是用 SQL 編寫的,但它們可能是內聯的, array_unique_stable是用 PL/pgSQL 編寫的,因此不能內聯。

第三,我無法在純 SQL 中提出解決方案,這讓我很困擾……

這確實可以使用純 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;
$$

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

返回

text_array | int_array 
-----------+-----------
{a,x,b}    | {10,1,5,8}

引用自:https://dba.stackexchange.com/questions/211501