Rastrear revisões no PostgreSQL
-
20-09-2019 - |
Pergunta
Eu tenho que acompanhar as revisões dos registros em uma tabela. O que fiz é criar uma segunda tabela que herda a primeira e adiciona um contador de revisão.
CREATE TABLE A (
id SERIAL,
foo TEXT,
PRIMARY KEY (id));
CREATE TABLE B (
revision INTEGER NOT NULL) INHERITS (A);
Em seguida, criei um gatilho que atualizaria a Tabela B sempre que A é inserido/atualizado. O que não consigo descobrir é como fazer com que o B.Revision mantenha uma "sequência" individual para cada ID.
Exemplo: A Tabela A tem 2 linhas, i & j.
Fui atualizado 3 vezes e devo ter 3 revisões: (1, 2, 3).
J foi atualizado 2 vezes e deve ter duas revisões: (1, 2).
Aqui está o que eu tenho até agora, talvez esteja seguindo o caminho errado e alguém pode me ajudar!
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();
Solução
Se você precisar dos números da versão apenas para fazer o pedido, e não precisa especificamente deles para ser um número inteiro que aumenta em um para cada identificador, a maneira mais fácil de fazer é usar uma sequência para a revisão e apenas deixá -la fazer o rastreamento para voce:
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();
Em seguida, faça as inserções como de costume:
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)
Então você pode obter todas as revisões de uma determinada linha como assim:
try=# select * from b where id = 1 order by revision;
id | foo | revision
----+-----+----------
1 | you | 1
1 | you | 3
(2 rows)
Outras dicas
Aqui está minha sugestão:
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;
Eu mudei o "se não for encontrado" em um Coalesce, que escolherá o "0" se não houver revisão existente. Em seguida, insiro na linha B, com a revisão incrementada.
Tenha cuidado com sua herança: você precisará usar a palavra -chave "apenas" para se limitar à tabela A ao selecionar e atualizar, como tal:
select * from only A
update only A set foo = ... where id = ...
Aqui está um pacote Rich Aduit Rich para o Postgres que eu usei no passado: Gatilho de auditoria. Ele rastreia o tipo de atualização (inserir, atualizar, excluir), bem como os valores antes e depois da atualização.
--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