2010-02-13 26 views
5

Tôi phải theo dõi các bản sửa đổi các bản ghi trong một bảng. Những gì tôi đã làm là tạo ra một bảng thứ hai kế thừa từ đầu tiên và thêm một bộ đếm sửa đổi.theo dõi các bản sửa đổi trong postgresql

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

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

Sau đó, tôi đã tạo trình kích hoạt sẽ cập nhật bảng B mỗi lần A được chèn/cập nhật. Những gì tôi không thể tìm ra là làm thế nào để làm cho B.revision giữ một "chuỗi" cá nhân cho mỗi id.

Ví dụ: bảng A có 2 hàng, i & j.
tôi đã được cập nhật 3 lần và cần có 3 bản sửa đổi: (1, 2, 3).
j đã được cập nhật 2 lần và cần có hai bản sửa đổi: (1, 2).

Đây là những gì tôi có cho đến nay, có thể tôi sẽ đi sai đường và ai đó có thể giúp tôi!

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

Trả lời

7

Nếu bạn cần các số phiên bản chỉ dành riêng cho đặt hàng và không đặc biệt cần chúng là số nguyên tăng một cho mỗi số nhận dạng, cách dễ nhất để thực hiện việc này là sử dụng chuỗi cho bản sửa đổi và chỉ cho phép theo dõi cho bạn:

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

Sau đó thực hiện các thao tác như bình thường:

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) 

Vì vậy, bạn có thể nhận được tất cả các phiên bản cho một hàng nhất định như vậy:

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

Điều này rất có ý nghĩa. Nó sẽ là tốt nhất mà OP thay đổi yêu cầu của mình để nhường chỗ cho điều này, bởi vì những thứ khác sẽ yêu cầu khóa như bạn đề cập đến. –

+1

Hrm. Và tôi chỉ nhận thấy rằng nó không hiển thị thông tin sửa đổi thực tế. Tôi chèn bản ghi tại r1 là "bar" và cập nhật nó trong r3 là "bạn", nhưng kết quả trong truy vấn cuối cùng hiển thị "bạn" cho cả hai sửa đổi. Để khắc phục điều đó, B không nên kế thừa từ A. sử dụng 'LIKE' thay vì' INHERITS' để tách chúng ra: 'TẠO BẢNG B (THÍCH A, phiên bản nối tiếp NOT NULL);'. – theory

+0

Hoặc sử dụng từ khóa "duy nhất". Nhưng có, nó có thể ít khó hiểu hơn để chỉ sử dụng các bảng riêng biệt. –

0

Dưới đây là gợi ý của tôi:

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; 

Tôi đã thay đổi "nếu không tìm thấy" vào một liên hiệp, mà sẽ chọn "0" nếu không có phiên bản hiện có. Sau đó, tôi chèn vào hàng B, với bản sửa đổi tăng dần.

Hãy cẩn thận với thừa kế của bạn: bạn sẽ cần phải sử dụng "chỉ" từ khóa để giới hạn mình vào bảng A khi chọn và cập nhật, như vậy:

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

Giải pháp này có điều kiện chủng tộc. Để tránh tình trạng chạy đua, bạn phải khóa tất cả các bản ghi trong B bằng 'id = NEW.id' trước khi bạn có thể thực hiện thao tác chèn. Việc sử dụng một chuỗi tránh tình trạng đua và không yêu cầu khóa. – theory

+0

Vì vậy, bạn nên thêm SELECT FOR UPDATE * FROM B WHERE id = NEW.id, cũng phải là COALESCE (MAX (sửa đổi) +1,0) để có được một id sửa đổi mới thay vì giống nhau. –

+0

Đồng ý, mã này sẽ không hoạt động với quyền truy cập đồng thời. Nếu muốn đối phó với các điều kiện chủng tộc, chúng tôi sẽ mất khả năng có một chuỗi liên tiếp trên mỗi 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

hãy xem http://stackoverflow.com/editing-help#code và cố gắng 'chỉnh sửa' câu trả lời của bạn, để mã đó xuất hiện. Tôi không biết cách chỉnh sửa câu trả lời của bạn, bởi vì tôi không thể làm theo những gì bạn đang làm ... – kratenko

0

Đây là một tính năng phong phú gói Aduit cho postgres rằng tôi đã sử dụng trong quá khứ: Audit Trigger. Nó theo dõi loại cập nhật (chèn, cập nhật, xóa) cũng như các giá trị trước và sau cho bản cập nhật.

Các vấn đề liên quan