UPSERT 一行取决于两列值的唯一组合

UPSERT a row depending on the unique combination of values of two columns

我在数据库中有以下 table:

CREATE TYPE status AS ENUM (
    'void',
    'steady',
    'transition'
);

CREATE TABLE relations (
    marker integer NOT NULL,
    related integer[] NOT NULL,
    status status DEFAULT 'void'::status NOT NULL,
    id serial -- pgAdmin requires primary key
);

ALTER TABLE ONLY relations
    ADD CONSTRAINT pkey_id PRIMARY KEY (id);

INSERT INTO relations (marker, related, status)
VALUES
(3, '{6}', 'steady'::status),
(3, '{2}', 'transition'::status),
(6, '{4}', 'void'::status),
(6, '{2}', 'steady'::status),
(4, '{2}', 'steady'::status),
(4, '{6}', 'void'::status);

这就是 table 的样子:

 marker | related |   status   | id 
--------+---------+------------+----
      3 | {6}     | steady     |  1
      3 | {2}     | transition |  2
      6 | {4}     | void       |  3
      6 | {2}     | steady     |  4
      4 | {2}     | steady     |  5
      4 | {6}     | void       |  6

marker/status 组合应该是唯一的,即使还没有相应的约束。这不是这里的问题。 我也有这个功能:

CREATE OR REPLACE FUNCTION update_relations(integer, integer, status) RETURNS void
    LANGUAGE plpgsql
    AS $_$
BEGIN
    update relations
        set related = array_append(related,
        (CASE
            WHEN marker =  THEN 
            WHEN marker =  THEN 
        END)
        )
    where
        marker in (,) AND
        status = ;
END;
$_$;

当我运行

SELECT update_relations(3, 4, 'void'::status);

然后我想要 marker 值为 4status 为 'void' 的行更新其 related 值并附加一个 3 到数组。因此,具有 marker = 3status = 'void'::status 的行应将 4 附加到其 related 数组。然而,这是结果:

 marker | related |   status   | id 
--------+---------+------------+----
      3 | {6}     | steady     |  1
      3 | {2}     | transition |  2
      6 | {4}     | void       |  3
      6 | {2}     | steady     |  4
      4 | {2}     | steady     |  5
      4 | {6,3}   | void       |  6

如您所见,包含 marker = 4status = 'void'::status 的行已按预期更新。由于没有满足 'marker = 3 and status = 'void'::status' 要求的相应行,因此不会发生更新。在这种情况下,我想插入一行,这样结果就是:

 marker | related |   status   | id 
--------+---------+------------+----
      3 | {6}     | steady     |  1
      3 | {2}     | transition |  2
      6 | {4}     | void       |  3
      6 | {2}     | steady     |  4
      4 | {2}     | steady     |  5
      4 | {6,3}   | void       |  6
      3 | {4}     | void       |  7

如果所需的 marker/status 组合不存在,我如何 UPSERT table?

PS: 我正在使用 postgres 9.4。

Postgres "upsert" 函数 ON CONFLICT UPDATE 适用于 9.5 及更高版本,但您使用的是 9.4。

如果你不担心并发,你可以做简单的:

if exists (select * from relations where marker in (,) AND status = ) then
    update  relations
    set     ...
    where   marker in (,) AND status = ;
else
    insert  into relations
            (marker, related, status)
    values  (, ARRAY[], );
end if

这种方法确实存在并发问题。请参阅 this question 以获得更好(和更复杂)的解决方案。