我有一个表格,其定义类似于以下内容(为了清楚起见,缩写):
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 。
答案 0 :(得分:4)
您可以将唯一约束创建为延迟,在这种情况下,您将在commit
交易时检查,而不是在执行insert
时检查:
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) deferrable initially deferred --<< here
);