使用函数(不是触发器)的旧值填充 Postgres table
Populate a Postgres table with old values from a function (not a trigger)
我有一个函数可以将唯一列 mytable.value
从 oldvalue
更新为 newvalue
。
问题是我想将 oldvalue
与旧的 modified
时间一起存储在单独的存档中 table。
我不知道如何使用普通的 plpgsql
函数 (Postgres 9.6) 来做到这一点。我想我知道如何使用触发器来做到这一点,但我希望仅在某些情况下填充存档 table,因此当显式调用函数时,而不是对 mytable
的每次修改。
create or replace function updatevalue(oldvalue text, newvalue text) returns void
language plpgsql
as $$
begin
update mytable set modified = now(), value = newvalue where value = oldvalue
end
$$;
如何修改上述函数以使用旧 modified
时间填充存档 table?由于可能存在竞争条件,我不想将它作为参数从应用程序传递给函数。
我觉得这个很简单,如果我理解正确的话。
所以,如果你想在 update/function 调用时存档每个旧值及其修改时间,那么你只需要 archive_table
结构:
mytable_pk_value int/bigint -- assumes that mytable have primary key/identity column, lets say "id"
mytable_modified_time -- with mytable.modified datatype
mytable_oldvalue -- with mytable.value datatype
那么在函数中你有:
...
begin
insert into archive_table
(mytable_pk_value, mytable_modified_time, mytable_oldvalue)
select id, modified, value
from mytable
where value = oldvalue;
update mytable set modified = now(), value = newvalue where value = oldvalue
end
...
关于“可能的竞争条件”:
如果你 运行 将 INSERT
和 UPDATE
语句分开,你就会得到你想要避免的东西:竞争条件。 INSERT
和 UPDATE
之间的旧行可能发生任何事情,它没有被锁定。
将两者都包含在事务中(如 建议的那样)不会 解决此问题。您可以使用 FOR UPDATE
锁定行,或者使用数据修改 CTE 仅访问一次行:
WITH upd AS (
UPDATE mytable n
SET modified = now() -- consider a trigger instead
, value = $newvalue -- provide value once
FROM mytable o
WHERE o.value = $oldvalue -- provide value once
AND n.value = o.value
RETURNING o.* -- returns pre-update row
)
INSERT INTO archive
SELECT * FROM upd;
mytable
中的行仅被访问 一次。没有竞争条件,速度也更快。相关:
- Return pre-UPDATE Column Values Using SQL Only - PostgreSQL Version
这是假设 mytable
和 archive
的结构相同,因此我从 INSERT
中省略了目标列列表。如果结构不相同或可以更改,请明确说明并改为拼写列。
你可以把它包装成一个(plpgsql或sql)函数或者直接执行它(作为准备好的语句)。
旁白:我不考虑手动设置 modified
,而是考虑使用触发器 ON UPDATE
或 ON INSERT OR UPDATE
自动处理此问题:
- Timestamp can't be saved - postgresql
备选方案:使用 FOR UPDATE
显式锁定
BEGIN; -- one transaction
INSERT INTO archive
SELECT *
FROM mytable
WHERE value = $oldvalue
FOR UPDATE; -- lock !
UPDATE mytable
SET modified = now()
, value = $newvalue
WHERE value = $oldvalue;
COMMIT;
如果将其包装在一个函数中,它会自动成为一个事务。
相关:
我有一个函数可以将唯一列 mytable.value
从 oldvalue
更新为 newvalue
。
问题是我想将 oldvalue
与旧的 modified
时间一起存储在单独的存档中 table。
我不知道如何使用普通的 plpgsql
函数 (Postgres 9.6) 来做到这一点。我想我知道如何使用触发器来做到这一点,但我希望仅在某些情况下填充存档 table,因此当显式调用函数时,而不是对 mytable
的每次修改。
create or replace function updatevalue(oldvalue text, newvalue text) returns void
language plpgsql
as $$
begin
update mytable set modified = now(), value = newvalue where value = oldvalue
end
$$;
如何修改上述函数以使用旧 modified
时间填充存档 table?由于可能存在竞争条件,我不想将它作为参数从应用程序传递给函数。
我觉得这个很简单,如果我理解正确的话。
所以,如果你想在 update/function 调用时存档每个旧值及其修改时间,那么你只需要 archive_table
结构:
mytable_pk_value int/bigint -- assumes that mytable have primary key/identity column, lets say "id"
mytable_modified_time -- with mytable.modified datatype
mytable_oldvalue -- with mytable.value datatype
那么在函数中你有:
...
begin
insert into archive_table
(mytable_pk_value, mytable_modified_time, mytable_oldvalue)
select id, modified, value
from mytable
where value = oldvalue;
update mytable set modified = now(), value = newvalue where value = oldvalue
end
...
关于“可能的竞争条件”:
如果你 运行 将 INSERT
和 UPDATE
语句分开,你就会得到你想要避免的东西:竞争条件。 INSERT
和 UPDATE
之间的旧行可能发生任何事情,它没有被锁定。
将两者都包含在事务中(如 FOR UPDATE
锁定行,或者使用数据修改 CTE 仅访问一次行:
WITH upd AS (
UPDATE mytable n
SET modified = now() -- consider a trigger instead
, value = $newvalue -- provide value once
FROM mytable o
WHERE o.value = $oldvalue -- provide value once
AND n.value = o.value
RETURNING o.* -- returns pre-update row
)
INSERT INTO archive
SELECT * FROM upd;
mytable
中的行仅被访问 一次。没有竞争条件,速度也更快。相关:
- Return pre-UPDATE Column Values Using SQL Only - PostgreSQL Version
这是假设 mytable
和 archive
的结构相同,因此我从 INSERT
中省略了目标列列表。如果结构不相同或可以更改,请明确说明并改为拼写列。
你可以把它包装成一个(plpgsql或sql)函数或者直接执行它(作为准备好的语句)。
旁白:我不考虑手动设置 modified
,而是考虑使用触发器 ON UPDATE
或 ON INSERT OR UPDATE
自动处理此问题:
- Timestamp can't be saved - postgresql
备选方案:使用 FOR UPDATE
显式锁定
BEGIN; -- one transaction
INSERT INTO archive
SELECT *
FROM mytable
WHERE value = $oldvalue
FOR UPDATE; -- lock !
UPDATE mytable
SET modified = now()
, value = $newvalue
WHERE value = $oldvalue;
COMMIT;
如果将其包装在一个函数中,它会自动成为一个事务。
相关: