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
值为 4
且 status
为 'void' 的行更新其 related
值并附加一个 3
到数组。因此,具有 marker = 3
和 status = '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 = 4
和 status = '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 以获得更好(和更复杂)的解决方案。
我在数据库中有以下 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
值为 4
且 status
为 'void' 的行更新其 related
值并附加一个 3
到数组。因此,具有 marker = 3
和 status = '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 = 4
和 status = '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 以获得更好(和更复杂)的解决方案。