2010-02-13 18 views
5

Devo tenere traccia delle revisioni dei record in una tabella. Quello che ho fatto è creare una seconda tabella che eredita dal primo e aggiunge un contatore di revisioni.revisioni traccia in postgresql

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

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

Quindi ho creato un trigger che aggiornava la tabella B ogni volta che A viene inserito/aggiornato. Quello che non riesco a capire è come rendere B.revision mantenere una "sequenza" individuale per ogni id.

Esempio: la tabella A ha 2 righe, i & j.
Sono stato aggiornato 3 volte e dovrebbe avere 3 revisioni: (1, 2, 3).
j è stato aggiornato 2 volte e dovrebbe avere due revisioni: (1, 2).

Ecco quello che ho finora, forse sto scendendo la strada sbagliata e qualcuno può aiutarmi!

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(); 

risposta

7

Se avete bisogno i numeri di versione solo per ordinazione, e non specificamente bisogno di loro per essere un numero intero che aumentano di uno per ogni identificatore, il modo più semplice per farlo è quello di utilizzare una sequenza per la revisione e lasciare che faccia il monitoraggio per voi:

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(); 

Quindi inserire gli inserimenti come al solito:

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) 

modo da poter ottenere tutte le revisioni per una determinata riga in questo modo:

try=# select * from b where id = 1 order by revision; 
    id | foo | revision 
    ----+-----+---------- 
     1 | you |  1 
     1 | you |  3 
    (2 rows) 
+0

Questo ha molto senso. Sarebbe meglio che l'OP cambi i suoi requisiti per fare spazio a questo, perché altrimenti le cose richiederebbero il blocco come si menziona. –

+1

Hrm. E ho appena notato che non mostra le informazioni di revisione effettive. Ho inserito il record su r1 come "bar" e lo ho aggiornato in r3 come "tu", ma i risultati in quest'ultima query mostrano "tu" per entrambe le revisioni. Per risolvere il problema, B non dovrebbe ereditare da A. usare 'LIKE' invece di' INHERITS' per disaccoppiarli: 'CREATE TABLE B (LIKE A, revision serial NOT NULL);'. – theory

+0

Oppure utilizzare la parola chiave "solo". Ma sì, potrebbe essere meno complicato usare solo tabelle separate. –

0

Ecco il mio suggerimento:

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; 

ho cambiato il "se non trovato" in un coalesce, che sceglierà il "0" se non v'è alcuna revisione esistente. Quindi, inserisco in B la riga, con la revisione incrementata.

stare attenti con la tua eredità: è necessario utilizzare la parola chiave "solo" per limitarsi alla Un tavolo al momento della selezione e l'aggiornamento, come ad esempio:

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

Questa soluzione ha una condizione di competizione. Per evitare le condizioni della gara, dovresti bloccare tutti i record in B con 'id = NEW.id' prima di poter inserire l'inserto. L'uso di una sequenza evita le condizioni della gara e quindi non richiede alcun blocco. – theory

+0

Quindi dovresti aggiungere un SELECT FOR UPDATE * FROM B WHERE id = NEW.id, inoltre dovrebbe essere COALESCE (MAX (revision) +1,0) per ottenere un nuovo id di revisione invece dello stesso. –

+0

Concordato, questo codice non funzionerebbe con l'accesso simultaneo. Se volessimo affrontare le condizioni della gara, perderemmo la possibilità di avere una singola sequenza consecutiva per ogni id. –

-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

per favore guarda http://stackoverflow.com/editing-help#code e prova a "modificare" la tua risposta, in modo che il codice rimanga impressionato. Non sapevo come modificare la tua risposta, perché non potevo seguire quello che stai facendo ... – kratenko

0

Ecco un ricco pacchetto di funzionalità per Aduit postgres che ho usato in passato: Audit Trigger. Tiene traccia del tipo di aggiornamento (inserimento, aggiornamento, eliminazione) e dei valori precedenti e successivi per l'aggiornamento.