仅当元素不存在时才将元素追加到数组

Roc*_*nja 6 postgresql postgresql-9.3

WITH upd AS (
  UPDATE univ.products
  SET source = source::int[] || _source
  WHERE project_id = _project_id AND product_id = _products AND NOT(_source = ANY(source))
  RETURNING id
)

, ins AS (
  INSERT INTO univ.products(project_id, product_id, source, tracked)
  SELECT _project_id, _product_id, ARRAY[_source], _tracked
  WHERE NOT EXISTS (SELECT 1 FROM upd) 
  RETURNING id
 )

SELECT id
FROM   upd NATURAL FULL OUTER JOIN ins 
INTO   _project_product_id;
Run Code Online (Sandbox Code Playgroud)

我的情况,我有这个函数,其中包含上述所有内容以及更多内容,我试图避免重复项目/产品对,但同时通过附加新源来更新源。现在每个产品/项目对的源必须是唯一的,这就是我添加的原因NOT(_source = ANY(source)),但如果这是真的,select 1 from up将不会返回任何内容,因此将尝试将记录插入数据库以避免这种情况,我添加了以下内容例外。

EXCEPTION WHEN UNIQUE_VIOLATION THEN
    EXIT;
END;
Run Code Online (Sandbox Code Playgroud)

这几乎是我想要的,但没有返回任何 id 有没有更好的方法来做到这一点?

我还注意到,如果我插入一条记录,那么我会插入具有不同源的相同记录(行id将为1),然后我插入另一条记录,行id将为3而不是2;

[编辑] 我通过执行以下操作解决了我的问题:

SET source = CASE WHEN NOT(source::int[] @> ARRAY[_source]) THEN source::int[] || _source ELSE source END
Run Code Online (Sandbox Code Playgroud)

有更好的解决方案吗?这个好像有点慢。

小智 1

它不是超级高效,但您可以将解决方案之类的东西定义为函数:

create or replace function set_append(anyarray, anyelement)
  returns anyarray
  immutable
  PARALLEL safe
  language sql as
$$
  select case when $2=any($1) then $1 else $1 || $2 end;
$$;
Run Code Online (Sandbox Code Playgroud)

您还可以使用它来定义聚合:

CREATE AGGREGATE set_agg( ANYELEMENT ) (
  SFUNC = set_append,
  STYPE = ANYARRAY,
  INITCOND = '{}');
Run Code Online (Sandbox Code Playgroud)

这将使您select set_agg(x order by y)能够控制您无法控制的不同元素的顺序select array_agg(distinct x order by y)