2009-11-25 26 views
20

(Lưu ý:. Cập nhật với câu trả lời thông qua dưới đây)PostgreSQL: UPDATE ngụ ý di chuyển trên phân vùng

Đối với một PostgreSQL 8.1 (hoặc mới hơn) bảng phân vùng, làm thế nào để xác định một UPDATE kích hoạt và thủ tục để "di chuyển" một bản ghi từ phân vùng này sang phân vùng khác, nếu UPDATE ngụ ý một sự thay đổi đối với trường ràng buộc xác định phân đoạn phân vùng?

Ví dụ, tôi đã là một bảng hồ sơ phân chia thành các hồ sơ hoạt động và không hoạt động như sau:

create table RECORDS (RECORD varchar(64) not null, ACTIVE boolean default true); 
create table ACTIVE_RECORDS (check (ACTIVE)) inherits RECORDS; 
create table INACTIVE_RECORDS (check (not ACTIVE)) inherits RECORDS; 

Các INSERT kích hoạt và làm việc chức năng cũng như: hồ sơ hoạt động mới được đưa vào một bảng, và mới hồ sơ không hoạt động trong một cái khác. Tôi muốn UPDATE s vào trường ACTIVE để "di chuyển" một bản ghi từ một bảng con cháu này sang bảng con khác, nhưng tôi gặp phải lỗi cho thấy điều này có thể không thực hiện được.

Kích hoạt đặc điểm kỹ thuật và thông báo lỗi:

pg=> CREATE OR REPLACE FUNCTION record_update() 
    RETURNS TRIGGER AS $$ 
    BEGIN 
     IF (NEW.active = OLD.active) THEN 
     RETURN NEW; 
     ELSIF (NEW.active) THEN 
     INSERT INTO active_records VALUES (NEW.*); 
     DELETE FROM inactive_records WHERE record = NEW.record; 
     ELSE 
     INSERT INTO inactive_records VALUES (NEW.*); 
     DELETE FROM active_records WHERE record = NEW.record; 
     END IF; 
     RETURN NULL; 
    END; 
    $$ 
    LANGUAGE plpgsql; 

pg=> CREATE TRIGGER record_update_trigger 
     BEFORE UPDATE ON records 
     FOR EACH ROW EXECUTE PROCEDURE record_update(); 

pg=> select * from RECORDS; 
record | active 
--------+-------- 
foo | t   -- 'foo' record actually in table ACTIVE_RECORDS 
bar | f   -- 'bar' record actually in table INACTIVE_RECORDS 
(2 rows) 

pg=> update RECORDS set ACTIVE = false where RECORD = 'foo'; 
ERROR: new row for relation "active_records" violates check constraint "active_records_active_check" 

Chơi với các thủ tục kích hoạt (trở về NULL và vân vân) gợi ý với tôi rằng các hạn chế được kiểm tra, và các lỗi nêu ra, trước khi kích hoạt của tôi được gọi, có nghĩa là cách tiếp cận hiện tại của tôi sẽ không hoạt động. Điều này có thể được nhận để làm việc?

UPDATE/ĐÁP

Dưới đây là thủ tục UPDATE cò tôi đã kết thúc sử dụng, thủ tục tương tự được gán cho mỗi của phân vùng. Tín dụng là hoàn toàn để Bell, mà câu trả lời đã cho tôi cái nhìn sâu sắc chìa khóa để kích hoạt trên các phân vùng:

CREATE OR REPLACE FUNCTION record_update() 
RETURNS TRIGGER AS $$ 
BEGIN 
    IF ((TG_TABLE_NAME = 'active_records' AND NOT NEW.active) 
     OR 
     (TG_TABLE_NAME = 'inactive_records' AND NEW.active)) THEN 
    DELETE FROM records WHERE record = NEW.record; 
    INSERT INTO records VALUES (NEW.*); 
    RETURN NULL; 
    END IF; 

    RETURN NEW; 
END; 
$$ 
LANGUAGE plpgsql; 
+0

"dụ" bạn là không đầy đủ: thiếu định nghĩa cho "partitioned_records"; bạn xác định trình kích hoạt cho "partitioned_records" nhưng chọn từ và cập nhật "RECORDS". –

+0

@Milen, cảm ơn - lỗi cut'n'paste. Sẽ khắc phục. – pilcrow

+0

Điểm của việc sử dụng phân vùng trong ngữ cảnh này khi bạn có thể sử dụng chỉ mục một phần là gì? – kalu

Trả lời

17

Nó có thể được thực hiện để làm việc, kích hoạt mà không di chuyển chỉ cần được xác định cho mỗi phân vùng, không phải là toàn bộ bảng. Vì vậy, bắt đầu như bạn đã làm cho các định nghĩa bảng và INSERT kích hoạt

CREATE TABLE records (
record varchar(64) NOT NULL, 
active boolean default TRUE 
); 

CREATE TABLE active_records (CHECK (active)) INHERITS (records); 
CREATE TABLE inactive_records (CHECK (NOT active)) INHERITS (records); 

CREATE OR REPLACE FUNCTION record_insert() 
RETURNS TRIGGER AS $$ 
BEGIN 
    IF (TRUE = NEW.active) THEN 
    INSERT INTO active_records VALUES (NEW.*); 
    ELSE 
    INSERT INTO inactive_records VALUES (NEW.*); 
    END IF; 
    RETURN NULL; 
END; 
$$ 
LANGUAGE plpgsql; 

CREATE TRIGGER record_insert_trigger 
BEFORE INSERT ON records 
FOR EACH ROW EXECUTE PROCEDURE record_insert(); 

... chúng ta hãy có một số dữ liệu thử nghiệm ...

INSERT INTO records VALUES ('FirstLittlePiggy', TRUE); 
INSERT INTO records VALUES ('SecondLittlePiggy', FALSE); 
INSERT INTO records VALUES ('ThirdLittlePiggy', TRUE); 
INSERT INTO records VALUES ('FourthLittlePiggy', FALSE); 
INSERT INTO records VALUES ('FifthLittlePiggy', TRUE); 

Bây giờ trigger trên phân vùng. Nếu kiểm tra NEW.active = OLD.active là ẩn trong việc kiểm tra giá trị của hoạt động vì chúng ta biết những gì được phép ở trong bảng ở nơi đầu tiên.

CREATE OR REPLACE FUNCTION active_partition_constraint() 
    RETURNS TRIGGER AS $$ 
    BEGIN 
     IF NOT (NEW.active) THEN 
     INSERT INTO inactive_records VALUES (NEW.*); 
     DELETE FROM active_records WHERE record = NEW.record; 
     RETURN NULL; 
     ELSE 
     RETURN NEW; 
     END IF; 
    END; 
    $$ 
    LANGUAGE plpgsql; 

CREATE TRIGGER active_constraint_trigger 
    BEFORE UPDATE ON active_records 
    FOR EACH ROW EXECUTE PROCEDURE active_partition_constraint(); 

CREATE OR REPLACE FUNCTION inactive_partition_constraint() 
    RETURNS TRIGGER AS $$ 
    BEGIN 
     IF (NEW.active) THEN 
     INSERT INTO active_records VALUES (NEW.*); 
     DELETE FROM inactive_records WHERE record = NEW.record; 
     RETURN NULL; 
     ELSE 
     RETURN NEW; 
     END IF; 
    END; 
    $$ 
    LANGUAGE plpgsql; 

CREATE TRIGGER inactive_constraint_trigger 
    BEFORE UPDATE ON inactive_records 
    FOR EACH ROW EXECUTE PROCEDURE inactive_partition_constraint(); 

... và kiểm tra kết quả ...

scratch=> SELECT * FROM active_records; 
     record  | active 
------------------+-------- 
FirstLittlePiggy | t 
ThirdLittlePiggy | t 
FifthLittlePiggy | t 
(3 rows) 

scratch=> UPDATE records SET active = FALSE WHERE record = 'ThirdLittlePiggy'; 
UPDATE 0 
scratch=> SELECT * FROM active_records; 
     record  | active 
------------------+-------- 
FirstLittlePiggy | t 
FifthLittlePiggy | t 
(2 rows) 

scratch=> SELECT * FROM inactive_records; 
     record  | active 
-------------------+-------- 
SecondLittlePiggy | f 
FourthLittlePiggy | f 
ThirdLittlePiggy | f 
(3 rows) 
+0

@Bell, câu trả lời hay (và mũ đẹp). Tôi nghĩ rằng bạn đã có nó, và nếu có, bạn sẽ nhận được dấu kiểm màu xanh lục khi tôi xác minh! – pilcrow

+0

Vì vậy, trong ngắn hạn, tôi cần phải áp dụng logic phân vùng một lần nữa trong một kích hoạt cho mỗi chèn trên mỗi phân vùng? –

+0

@David Nathan - No. Logic cho các hoạt động INSERT (hoặc COPY) được cung cấp bởi Postgres.Câu hỏi và giải pháp là về việc xử lý UPDATE khi bản ghi được cập nhật sẽ nằm trong một phân vùng khác với bản gốc. – Bell

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