2011-10-30 6 views
5

Ho un trigger PostgreSQL su creazione che reindirizza fondamentalmente gli inserimenti in sotto-tabelle. Una volta inserito il record, desidero ABORT la richiesta per evitare dati duplicati. L'unico modo (che io sappia) per farlo è quello di restituire NULL nel trigger. Il problema è che ho bisogno che il record venga restituito, così posso ottenere l'ID. Se ritorno NULL, ottengo ... NULL.Trigger PostgreSQL che non restituisce nulla

Qualche idea su come è possibile che un trigger interrompa un'operazione mentre restituisce qualcosa di diverso da NULL?

+0

Così si sta cercando di costruire un trigger sulla tabella X che inserisce i dati in Y e Z, impedisce qualsiasi cosa, da essere inserito nel X, ma restituisce qualche ID? –

+0

Sì, sto partizionando i dati in tabelle ereditate. Quindi non voglio una riga duplicata nella tabella padre. Fondamentalmente voglio che questo processo sia completamente trasparente. Hai a che fare con la tabella genitore, i trigger fanno tutto dietro le quinte. Non cambia nulla dall'esterno. –

risposta

5

La tua domanda lascia spazio all'interpretazione. Per come la intendo, si desidera la clausola RETURNING del comando INSERT per restituire il valore della chiave primaria generata da una sequenza.

Ci sono altri modi per raggiungere questo obiettivo. Come usare nextval() per ottenere il prossimo id dalla sequenza precedente e inserire la riga con lo id enunciato.
O currval()/lastval() per ottenere il valore ottenuto più recentemente per una sequenza/qualsiasi sequenza nella sessione corrente. Continua in questa risposta correlato:
PostgreSQL next value of the sequences?

Si potrebbe anche usare un RULE ... INSTEAD .. per questo scopo.

Ma, per rispondere alla tua domanda - se è, in realtà, la tua domanda: può essere fatto utilizzando due trigger. Uno BEFORE, uno AFTER INSERT. Entrambi vengono attivati ​​in una transazione per definizione, quindi la riga fantasma nella prima tabella non è mai visibile a nessuno (eccetto i trigger).

Demo:

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

Chiamata in 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

Grazie! Questo è esattamente il risultato che volevo. La mia preoccupazione è il rendimento, l'aggiunta di una riga e quindi l'eliminazione. So che non è un grande successo, sembra solo disordinato. Non è possibile farlo in altro modo? Grazie per l'aiuto. –

+0

Sono sicuro che ci sono altri modi, ma è quello che hai chiesto. Ho suggerito di usare nextval() nella mia risposta. Hai visto che? (Collegamento Plus.) Ecco come potrei farlo. Il lato negativo con 'nextval()': un'altra chiamata al server. Quindi dipende dalle circostanze che saranno più veloci. Il mio esempio è una soluzione, ma dovrebbe essere perfettamente sicuro da usare. –