2010-02-13 4 views
5

Мне нужно отслеживать изменения записей в таблице. То, что я сделал, это создать вторую таблицу, которая наследуется от первой и добавляет счетчик ревизий.отслеживание изменений в postgresql

CREATE TABLE A (
id SERIAL, 
foo TEXT, 
PRIMARY KEY (id)); 

CREATE TABLE B (
revision INTEGER NOT NULL) INHERITS (A); 

Затем я создал триггер, который будет обновлять таблицу B каждый раз, когда вставлен/обновлен A. Что я не могу понять, так это то, как заставить B.revision сохранять индивидуальную «последовательность» для каждого идентификатора.

Пример: таблица A имеет 2 строки, i & j.
i был обновлен 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(); 

ответ

7

Если вам нужны номера версии только для заказывая и не нуждаются в том, чтобы они были целыми числами, которые увеличиваются по одному для каждого идентификатора, самый простой способ сделать это - использовать последовательность для пересмотра и просто дать ему отслеживание для вас:

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) 
+0

Это имеет смысл. Было бы лучше, если бы ОП изменил его требования, чтобы освободить место для этого, потому что иначе все будет требовать блокировки, как вы упомянули. –

+1

Hrm. И я только заметил, что он не отображает фактическую информацию о пересмотре. Я вставил запись в r1 как «bar» и обновил ее в r3 как «вы», но результаты этого последнего запроса показывают «вы» для обеих версий. Чтобы исправить это, B не должен наследовать от A. используйте 'LIKE' вместо' INHERITS', чтобы отделить их: 'CREATE TABLE B (LIKE A, серийный выпуск NOT NULL);'. – theory

+0

Или используйте ключевое слово «только». Но да, возможно, было бы немного запутать просто использование отдельных таблиц. –

0

Вот мое предложение:

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; 

Я изменил «если не найден» в COALESCE, что подберут «0», если нет существующих версий. Затем я вставляю в B строку с добавленной ревизией.

Будьте осторожны с наследованием: вам нужно будет использовать «только» ключевое слово, чтобы ограничить себя А таблицы при выборе и обновления, как таковые:

select * from only A 
update only A set foo = ... where id = ... 
+0

Данное решение имеет состояние гонки. Чтобы избежать состояния гонки, вам нужно будет заблокировать все записи в B с помощью 'id = NEW.id', прежде чем вы сможете сделать вставку. Использование последовательности позволяет избежать состояния гонки и поэтому не требует блокировки. – theory

+0

Итак, вы должны добавить SELECT FOR UPDATE * FROM B WHERE id = NEW.id, также он должен быть COALESCE (MAX (ревизия) +1,0), чтобы получить новый идентификатор ревизии, а не тот же. –

+0

Согласен, этот код не будет работать с одновременным доступом. Если вы хотите иметь дело с условиями гонки, мы потеряем способность иметь одну последовательную последовательность на один идентификатор. –

-1
--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 
+0

, пожалуйста, посмотрите http://stackoverflow.com/editing-help#code и попробуйте «отредактировать» свой ответ, чтобы код торчал. Я не знал, как отредактировать ваш ответ, потому что я не мог следить за тем, что вы делаете ... – kratenko

0

Вот многофункциональный Aduit пакет для Postgres, которые я использовал в прошлом: Audit Trigger. Он отслеживает тип обновления (вставка, обновление, удаление), а также значения до и после для обновления.

 Смежные вопросы

  • Нет связанных вопросов^_^