2009-11-25 9 views
20

(Nota:. Aggiornato con risposta adottata sotto)PostgreSQL: UPDATE implica muoversi tra le partizioni

Per un PostgreSQL 8.1 (o successivo) tabella partizionata, come si fa a definire un UPDATE grilletto e procedura per "spostare" un record da una partizione all'altra, se lo UPDATE implica una modifica al campo vincolato che definisce la separazione della partizione?

Per esempio, ho un record tabella partizionata in record attivi e inattivi in ​​questo modo:

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; 

I INSERT di trigger e il lavoro funzionano bene: nuovi record attivi vengono messe in una tabella, e nuovi record inattivi in un altro. Vorrei UPDATE s nel campo ACTIVE per "spostare" un record da una tabella discendente all'altra, ma sto riscontrando un errore che suggerisce che ciò potrebbe non essere possibile.

specifiche ed errori

trigger messaggio:

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" 

Giocando con la procedura di attivazione (ritorno NULL e così via) mi fa pensare che il vincolo viene controllato, e l'errore sollevato, prima che il mio grilletto viene richiamato, il che significa che il mio attuale approccio non funzionerà. Questo può essere fatto funzionare?

UPDATE/RISPOSTA

La procedura seguente UPDATE grilletto ho finito per usare la stessa procedura assegnato a ciascuna delle partizioni. Il credito è interamente a Bell, la cui risposta mi ha dato l'intuizione chiave per attivare sulle partizioni:

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

Il tuo "esempio" è incompleta: definizione mancante per "partitioned_records"; si definisce un trigger per "partitioned_records" ma si seleziona e si aggiorna "RECORDS". –

+0

@ Milen, grazie - errori cut'n'paste. Rimedi. – pilcrow

+0

Qual è il punto di utilizzo di una partizione in questo contesto quando è possibile utilizzare un indice parziale? – kalu

risposta

17

Può essere fatto per lavorare, il grilletto che fa la mossa solo deve essere definito per ciascuna partizione, non il tavolo intero Così inizia come avete fatto per le definizioni di tabella e il trigger 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(); 

... diamo alcuni dati di test ...

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

Ora i trigger sulle partizioni. Il se NEW.active = controllo OLD.active è implicito nel controllo del valore di attivo poiché sappiamo cosa è permesso essere nella tabella in primo luogo.

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

... e verificare i risultati ...

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, bella risposta (e bel cappello). Penso che tu ce l'abbia, e, in tal caso, riceverai il segno di spunta verde una volta verificato! – pilcrow

+0

Quindi, in breve, ho bisogno di applicare nuovamente la logica di partizione in un trigger per ogni inserto su ogni partizione? –

+0

@David Nathan - No. La logica per le operazioni INSERT (o COPY) è fornita da Postgres.La domanda e la soluzione riguardano la gestione di UPDATE quando il record aggiornato si trova in una partizione diversa dall'originale. – Bell