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

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

本问题已经有最佳答案,请猛点这里访问。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);

这就是表格的样子:

1
2
3
4
5
6
7
8
 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组合也应该是唯一的。 这不是问题。
我也有这个功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
$_$;

我跑的时候

1
SELECT update_relations(3, 4, 'void'::STATUS);

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

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

1
2
3
4
5
6
7
8
9
 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?

PS:我正在使用postgres 9.4。


Postgres"upsert"函数ON CONFLICT UPDATE为9.5或更高版本,但你的版本是9.4。

如果你不担心并发,你可以做到这一点:

1
2
3
4
5
6
7
8
9
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

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