2009-11-25 28 views
20

(注:下面通过回答更新)PostgreSQL的:UPDATE暗示分区间移动

对于一个PostgreSQL 8.1(或更高版本)分区表,一个人如何定义UPDATE触发器和程序“移动”从一个分区到另一个分区的记录,如果UPDATE意味着对定义分区隔离的约束字段的更改?

例如,我已经像这样划分为活动和非活动记录表中的记录:

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; 

INSERT触发和功能工作做好:新的活动记录,则会保留一个表,以及新的非活动记录在另一个。我想UPDATE s到活动字段“移动”记录从一个后代表到另一个,但遇到一个错误,这表明这可能是不可能的。

触发规范和错误消息:

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" 

与触发程序播放(返回NULL等)建议,我认为约束检查,错误引发,被调用我的触发之前,这意味着我目前的做法是行不通的。这可以得到工作吗?

UPDATE/ANSWER

下面是UPDATE触发器过程我最终使用,分配给每个分区的相同的过程。信用是完全是Bell,他的回答给我的重要观点对分区触发:

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

你的 “榜样” 是不完整:缺少定义 “partitioned_records”;你定义一个“partitioned_records”的触发器,但是选择并更新“RECORDS”。 –

+0

@Milen,谢谢 - cut'n'paste错误。将补救。 – pilcrow

+0

当你可以使用部分索引时,在这种情况下使用分区有什么意义? – kalu

回答

17

可以做的工作,那不动扳机只需要为每个分区定义,而不是整个桌子。因此,开始像你一样的表定义和INSERT触发器

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

...让我们有一些测试数据...

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

现在分区上的触发器。如果NEW.active = OLD.active检查隐含在检查活动的值中,因为我们知道首先允许表中的内容。

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

...并测试结果...

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

@贝尔,很好的答案(和漂亮的帽子)。我认为你已经知道了,如果是这样,一旦我确认,你会得到绿色的选中标记! – pilcrow

+0

因此,总之,我需要在每个分区的每个插入的触发器中再次应用分区逻辑? –

+0

@David Nathan - 不,INSERT(或COPY)操作的逻辑由Postgres提供。问题和解决方案是关于处理更新时更新的记录将在与原始不同的分区。 – Bell