在 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

Postgres-XL 向后兼容 PostgreSQL?

在 Greenplum(Postgres 8.4)中进行多行更新时跟踪错误记录?

postgres安装数据库集群初始化失败(Postgresql Version 9.4.4)