2009-11-25 2 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 в поле ACTIVE, чтобы «переместить» запись из одной таблицы потомков в другую, но встречаю ошибку, которая предполагает, что это может быть невозможно.

Trigger спецификация и сообщение об ошибке:

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/ОТВЕТ

Ниже процедура 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, thanks - cut'n'paste ошибки. Будет исправлено. – pilcrow

+0

Какой смысл использовать раздел в этом контексте, если вы можете использовать частичный индекс? – kalu

ответ

17

Это может быть сделано, чтобы работать, триггер, что делает этот шаг просто должен быть определен для каждого раздела, а не целая таблица. Так начните, как вы делали для определения таблиц и ВСТАВИТЬ триггер

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 подразумевается при проверке значения 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

@Bell, хороший ответ (и хорошая шляпа). Я думаю, что у вас это есть, и если да, то вы получите зеленую галочку, как только я подтвержу! – pilcrow

+0

Итак, короче, мне нужно снова применить логику раздела в триггере для каждой вставки на каждом разделе? –

+0

@ Давид Натан - Нет. Логика операций INSERT (или COPY) предоставляется Postgres.Вопрос и решение касаются обработки UPDATE, когда обновленная запись будет находиться в другом разделе от оригинала. – Bell

Смежные вопросы