【发布时间】:2015-05-12 22:00:56
【问题描述】:
我有一个表,其定义类似于以下内容(为清楚起见进行了精简):
CREATE TABLE fns(
id serial,
start_date timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
end_date timestamptz,
name text NOT NULL,
parent_id integer,
PRIMARY KEY (id),
FOREIGN KEY (parent_id) REFERENCES fns(id),
UNIQUE(name)
);
当UPDATE 发生时,我希望正在“更新”的行将其end_date 设置为CURRENT_TIMESTAMP 并创建一个新行(基于旧行)并设置其start_date到CURRENT_TIMESTAMP。例如:
UPDATE之前
| id | start_date | end_date | name | parent_id |
|----|-------------------------|----------|-------|-----------|
| 1 | April, 01 2015 00:00:00 | (null) | fns_a | (null) |
UPDATE之后的期望状态
| id | start_date | end_date | name | parent_id |
|----|-------------------------|-------------------------|-------------|-----------|
| 1 | April, 01 2015 00:00:00 | April, 02 2015 00:00:00 | fns_a [old] | (null) |
| 2 | April, 02 2015 00:00:00 | (null) | fns_a | 1 |
我遇到了name 列的唯一约束问题。这是我的触发器的当前状态:
CREATE OR REPLACE FUNCTION enfore_fns_immutability() RETURNS trigger AS $func$
BEGIN
-- 'Turn off' old record.
OLD.end_date = CURRENT_TIMESTAMP;
OLD.name = OLD.name || ' [old]';
-- Create the new record.
INSERT INTO fns(start_date, name, parent_id)
VALUES(CURRENT_TIMESTAMP, NEW.name, OLD.id); -- <-- unique violation
RETURN OLD;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER tg_fns_bi
BEFORE UPDATE ON fns
FOR EACH ROW
EXECUTE PROCEDURE enforce_fns_immutability();
据我了解,这是失败的,因为尚未提交对 OLD.name 的更新,因为包含事务尚未提交。我正在努力想办法解决它,但感觉必须有一个优雅的解决方案!我考虑过的一些解决方案:
- 临时表(感觉这个用例太重了)。
- 使用
AFTER UPDATE触发器(与事务显然尚未提交的问题相同)。
我正在使用 Postgres 9.4.1。
【问题讨论】:
-
有一个来自@RadekPostołowicz 的已删除答案,我认为这也是有效的:您还可以使用条件@ 创建部分唯一索引(与唯一约束相反) 987654335@。这样您甚至可以保留原始名称,而无需在其上附加
[old]。 -
@a_horse_with_no_name 谢谢 :) 我认为部分唯一约束对我们不起作用,因为有两次编辑。在这种情况下,我们将有两条记录,其中
parent_id is null. -
顺便说一句,在实际代码中,我们在修改后的名称中使用了时间戳组件,因此我们最终不会有两个被称为
fns_a [old]:-)
标签: postgresql