2009-11-25 16 views
20

(Nota:. Actualizado con la respuesta adoptada más adelante)PostgreSQL: ACTUALIZACIÓN implica movimiento a través de particiones

Para una PostgreSQL 8.1 (o posterior) tabla con particiones, ¿cómo se define un UPDATE gatillo y el procedimiento para "mover" un registro de una partición a la otra, si el UPDATE implica un cambio en el campo restringido que define la segregación de la partición?

Por ejemplo, tengo una tabla con particiones registros en los registros activos e inactivos, así:

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; 

Los INSERT de activación y función de trabajo así: nuevos registros activos llegar a poner en una mesa, y los nuevos registros inactivos en otro. Me gustaría UPDATE s al campo ACTIVO para "mover" un registro de una tabla descendiente a la otra, pero estoy encontrando un error que sugiere que esto puede no ser posible.

especificación y error

gatillo mensaje:

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" 

Jugando con el procedimiento de disparo (volviendo NULL y así sucesivamente) me sugiere que la restricción está marcada, y elevó el error, antes de invocar mi gatillo, lo que significa que mi enfoque actual no funcionará ¿Esto se puede poner a trabajar?

ACTUALIZACIÓN/RESPUESTA

A continuación se muestra el procedimiento UPDATE gatillo Terminé usando el mismo procedimiento asignado a cada una de las particiones. Crédito es enteramente a Bell, cuya respuesta me dio la idea clave para disparar sobre las particiones:

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

su "ejemplo" es incompleta: definición de "falta partitioned_records"; usted define un disparador para "particion_records" pero selecciona y actualiza "RECORDS". –

+0

@Milen, gracias - errores de corte de dientes pasados. Remediaré – pilcrow

+0

¿De qué sirve usar una partición en este contexto cuando podría usar un índice parcial? – kalu

Respuesta

17

Puede ser hecho para trabajar, el detonante que hace el movimiento sólo tiene que ser definido para cada partición, no el toda la mesa Así que empieza a como lo hizo para las definiciones de tabla y el desencadenador 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(); 

... vamos a tener algunos datos de prueba ...

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

Ahora los disparadores en las particiones. La comprobación if NEW.active = OLD.active está implícita al verificar el valor de activo ya que sabemos en primer lugar lo que está permitido en la tabla.

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

... y probar los resultados ...

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, buena respuesta (y buen sombrero). ¡Creo que lo tienes y, de ser así, obtendrás la marca de verificación verde una vez que lo haya verificado! – pilcrow

+0

Así que, en resumen, ¿debo volver a aplicar la lógica de partición en un desencadenante para cada inserción en cada partición? –

+0

@David Nathan - No. La lógica para las operaciones INSERT (o COPY) es proporcionada por Postgres.La pregunta y la solución se refieren a la ACTUALIZACIÓN cuando el registro actualizado se encuentre en una partición diferente del original. – Bell

Cuestiones relacionadas