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

时间:2018-06-09 19:48:02

标签: sql postgresql upsert

我在数据库中有以下表格:

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);

这就是表格的样子:

 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 = $1 THEN $2
            WHEN marker = $2 THEN $1
        END)
        )
    where
        marker in ($1,$2) AND
        status = $3;
END;
$_$;

当我跑步时

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

然后我希望marker值为4status为'无效'的行更新其related值并将3附加到marker = 3阵列。因此,具有status = 'void'::status4的行应将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 = 4

如您所见,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 = 3和status ='void':: status`要求的相应行,因此不会发生更新。在这种情况下,我想插入一行,结果将是:

marker/status

如果不存在所需的{{1}}组合,我该怎样对表格进行UPSERT?

PS:我正在使用postgres 9.4。

1 个答案:

答案 0 :(得分:1)

Postgres“upsert”函数ON CONFLICT UPDATE适用于9.5及更高版本,但您的版本为9.4。

如果您不担心并发性,可以这么做:

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

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