在 postgresql 中跟踪修订
Posted
技术标签:
【中文标题】在 postgresql 中跟踪修订【英文标题】:track revisions in postgresql 【发布时间】:2011-01-16 13:55:14 【问题描述】:我必须跟踪表中记录的修订。我所做的是创建第二个表,它继承自第一个表并添加一个修订计数器。
CREATE TABLE A (
id SERIAL,
foo TEXT,
PRIMARY KEY (id));
CREATE TABLE B (
revision INTEGER NOT NULL) INHERITS (A);
然后我创建了一个触发器,每次插入/更新 A 时都会更新表 B。我想不通的是如何让 B.revision 为每个 id 保留一个单独的“序列”。
示例:表 A 有 2 行,i & j。 我已经更新了 3 次,应该有 3 次修订:(1, 2, 3)。 j 已经更新了 2 次,应该有两个修订版:(1, 2)。
这是我到目前为止所拥有的,也许我走错了路,有人可以帮助我!
CREATE OR REPLACE FUNCTION table_update() RETURNS TRIGGER AS $table_update$
DECLARE
last_revision INTEGER;
BEGIN
SELECT INTO last_revision MAX(revision) FROM B WHERE id = NEW.id;
IF NOT FOUND THEN
last_revision := 0;
END IF;
INSERT INTO B SELECT NEW.*;
RETURN NEW;
END;
$table_update$ LANGUAGE plpgsql;
CREATE TRIGGER table_update
AFTER INSERT OR UPDATE ON A
FOR EACH ROW EXECUTE PROCEDURE table_update();
【问题讨论】:
【参考方案1】:这是一个功能丰富的 Aduit 包,用于我过去使用过的 postgres:Audit Trigger。它跟踪更新的类型(插入、更新、删除)以及更新的前后值。
【讨论】:
【参考方案2】:--THIS TABLE AUTOMATICALLY INCREMENT THE COLUMN VALUES USING TRIGGER
CREATE TABLE emp_table(
emp_id int not null,
emp_name varchar not null,
emp_rollno int not null,
primary key(emp_id)
);
--Now create table with three column and emp_id is primary key
--and emp_rollno both are automatically increment in trigger is fired
CREATE or REPLACE FUNCTION emp_fun() RETURNS TRIGGER AS $BODY$
--creating function emp_fun()
DECLARE
BEGIN
IF(tg_op='INSERT') THEN
NEW.emp_id=COALESCE((SELECT MAX(emp_id)+1 FROM emp_table), 1);
NEW.emp_rollno=COALESCE((SELECT MAX(emp_rollno)+1 FROM emp_table), 1);
--trigger is fired values is automatically increment
END IF;
IF tg_op='DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF;
END; $BODY$LANGUAGE PLPGSQL
CREATE TRIGGER emp_fun BEFORE INSERT ON
emp_table FOR EACH ROW EXECUTE PROCEDURE emp_fun();
INSERT INTO emp_table(emp_name) VALUES('BBB');
--insert the value tanle emp_table
SELECT * FROM emp_table
-- Check the result
【讨论】:
请查看***.com/editing-help#code 并尝试edit
您的答案,以便代码突出显示。我不知道如何编辑你的答案,因为我无法理解你在做什么......【参考方案3】:
如果您只需要版本号来订购,并且不特别需要它们是每个标识符加一的整数,那么最简单的方法是使用修订序列并让它为您进行跟踪:
CREATE TABLE A (
id SERIAL,
foo TEXT,
PRIMARY KEY (id)
);
CREATE TABLE B ( revision SERIAL NOT NULL) INHERITS (A);
CREATE OR REPLACE FUNCTION table_update() RETURNS TRIGGER AS $table_update$
BEGIN
INSERT INTO B SELECT NEW.*;
RETURN NEW;
END;
$table_update$ LANGUAGE plpgsql;
CREATE TRIGGER table_update
AFTER INSERT OR UPDATE ON A
FOR EACH ROW EXECUTE PROCEDURE table_update();
然后像往常一样插入:
try=# insert into a (foo) values ('bar');
INSERT 0 1
try=# insert into a (foo) values ('bar');
INSERT 0 1
try=# update a set foo = 'you' where id = 1;
UPDATE 2
try=# select * from b;
id | foo | revision
----+-----+----------
2 | bar | 2
1 | you | 1
1 | you | 3
(3 rows)
因此,您可以像这样获取给定行的所有修订:
try=# select * from b where id = 1 order by revision;
id | foo | revision
----+-----+----------
1 | you | 1
1 | you | 3
(2 rows)
【讨论】:
这很有意义。最好是 OP 更改他的要求以为此腾出空间,因为否则事情将需要您提到的锁定。 人力资源部。我只是注意到它没有显示实际的修订信息。我将 r1 中的记录插入为“bar”,并在 r3 中将其更新为“you”,但最后一个查询的结果显示两个修订版的“you”。为了解决这个问题,B 不应该从 A 继承。使用LIKE
而不是 INHERITS
来解耦它们:CREATE TABLE B ( LIKE A, revision serial NOT NULL);
。
或者使用“only”关键字。但是,是的,只使用单独的表可能不会那么混乱。
好吧,您可以在 PostgreSQL 8.4 上使用窗口函数来获取数字序列的修订:select *, rank() over (partition by id order by revision) as version from b where id = 1;
。
是的,注意唯一的关键字,解决您的第二条评论,正如 small_duck 所说。【参考方案4】:
这是我的建议:
CREATE OR REPLACE FUNCTION table_update() RETURNS TRIGGER AS $table_update$
DECLARE
last_revision INTEGER;
BEGIN
SELECT INTO last_revision coalesce(MAX(revision), 0) FROM B WHERE id = NEW.id;
INSERT INTO B SELECT NEW.*, last_revision + 1;
RETURN NEW;
END;
$table_update$ LANGUAGE plpgsql;
我将“如果未找到”更改为合并,如果没有现有修订,它将选择“0”。然后,我在 B 行中插入增加了修订的行。
小心你的继承:在选择和更新时,你需要使用“only”关键字将自己限制在A表中,如下所示:
select * from only A
update only A set foo = ... where id = ...
【讨论】:
此解决方案存在竞争条件。为了避免竞争条件,您必须在插入之前使用id = NEW.id
锁定 B 中的所有记录。使用序列避免了竞争条件,因此不需要锁定。
所以你应该添加一个 SELECT FOR UPDATE * FROM B WHERE id = NEW.id ,它也应该是 COALESCE(MAX(revision)+1,0) 来获得一个新的修订 id 而不是一样。
同意,此代码不适用于并发访问。如果要处理竞争条件,我们将失去每个 id 拥有单个连续序列的能力。以上是关于在 postgresql 中跟踪修订的主要内容,如果未能解决你的问题,请参考以下文章
从Postgres95到PostgreSQL9.5:新版亮眼特性
如何在 postgresql 中使用 postgres_fdw 从另一个数据库中获取记录
设置 PostgreSQL,无法连接到数据库 postgres