【问题标题】:track revisions in postgresql在 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();

【问题讨论】:

    标签: postgresql triggers plpgsql sql-function revisions


    【解决方案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
      

      【讨论】:

      • 请查看stackoverflow.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 拥有单个连续序列的能力。
      猜你喜欢
      • 2013-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-25
      • 2018-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多