2011-10-30 16 views
5

Tengo un desencadenador PostgreSQL en la creación que básicamente redirige las inserciones a las tablas secundarias. Una vez que inserte el registro, quiero ABORTAR la solicitud para evitar la duplicación de datos. La única forma (que yo sepa) de hacer esto es devolver NULL en el disparador. El problema es que necesito que me devuelvan el registro para que pueda obtener la identificación. Si devuelvo NULL, obtengo ... NULL.El desencadenador PostgreSQL no devuelve nada

¿Alguna idea de cómo puedo hacer que un desencadenador anule una operación mientras aún devuelve algo que no sea NULL?

+0

Así que usted está tratando de construir un disparador en la tabla X que inserta los datos en Y y Z, impide que cualquier cosa sea insertado en X, pero devuelve una identificación? –

+0

Sí, estoy dividiendo los datos en tablas heredadas. Entonces no quiero una fila duplicada en la tabla padre. Básicamente quiero que este proceso sea completamente transparente. Usted trata con la tabla padre, los factores desencadenantes hacen todo detrás de las escenas. Nada cambia desde afuera mirando hacia adentro. –

Respuesta

5

Su pregunta deja espacio para la interpretación. Tal como lo entiendo, quiere que la cláusula RETURNING del comando INSERT devuelva el valor de la clave primaria generada por una secuencia.

Hay otras formas de lograr esto. Como usar nextval() para obtener el siguiente id de la secuencia de antemano e insertar la fila con el id explicado.
O currval()/lastval() para obtener el último valor obtenido para una secuencia/cualquier secuencia en la sesión actual. Más en esta respuesta relacionada:
PostgreSQL next value of the sequences?

También es posible usar un RULE ... INSTEAD .. para este propósito.

Pero, para responder a su pregunta, si esa es, de hecho, su pregunta: se puede hacer utilizando dos desencadenantes. Una BEFORE, una AFTER INSERT. Ambos se activan en una transacción por definición, por lo que la fila fantasma en su primera tabla nunca es visible para nadie (excepto los desencadenantes).

Demostración:

CREATE TABLE x (
    id serial PRIMARY KEY -- note the serial col. 
,name text 
); 

CREATE TABLE y (
    id integer PRIMARY KEY 
,name text 
); 


CREATE OR REPLACE FUNCTION trg_x_insbef() 
    RETURNS trigger AS 
$func$ 
BEGIN 
    INSERT INTO y SELECT (NEW).*; -- write to other table 
    RETURN NEW; 
END 
$func$ LANGUAGE plpgsql; 

CREATE TRIGGER insbef 
    BEFORE INSERT ON x 
    FOR EACH ROW EXECUTE PROCEDURE trg_x_insbef(); 


CREATE OR REPLACE FUNCTION trg_x_insaft() 
    RETURNS trigger AS 
$func$ 
BEGIN 
    DELETE FROM x WHERE id = NEW.id; -- delete row again. 
    RETURN NULL; 
END 
$func$ LANGUAGE plpgsql; 

CREATE TRIGGER insaft 
    AFTER INSERT ON x 
    FOR EACH ROW EXECUTE PROCEDURE trg_x_insaft(); 

llamada en psql:

db=# INSERT INTO x (name) values('phantom') RETURNING id; 
id 
---- 
    1 
(1 row) 

INSERT 0 1 

db=# SELECT * FROM x; 
id | name 
----+------ 
(0 rows) 


db=# SELECT * FROM y; 
id | name 
----+--------- 
    1 | phantom 
(1 row) 
+0

¡Gracias! Este es el resultado exacto que quería. Mi preocupación es el rendimiento, agregando una fila y luego eliminándola. Sé que no es un gran éxito, simplemente parece desordenado. No es posible hacer esto de otra manera? Gracias por la ayuda. –

+0

Estoy seguro de que hay otras formas, pero eso es lo que pediste. Insinué usar nextval() en mi respuesta. ¿Viste eso? (Más enlace.) Así es como podría hacerlo. La desventaja con 'nextval()': una llamada más al servidor. Entonces depende de las circunstancias que serán más rápidas. Mi ejemplo es una solución, pero debe ser perfectamente seguro de usar. –

Cuestiones relacionadas