2011-10-30 11 views
5

Mam wyzwalacz PostgreSQL przy tworzeniu, który zasadniczo przekierowuje inserty do pod-tabel. Po wstawieniu rekordu chcę ABORTować żądanie, aby uniknąć duplikowania danych. Jedynym sposobem (o którym wiem), aby to zrobić, jest zwrócenie NULL w wyzwalaczu. Problem polega na tym, że muszę odzyskać rekord, aby uzyskać identyfikator. Jeśli wrócę NULL, otrzymam ... NULL.Wyzwalacz PostgreSQL nie zwracający niczego

Każdy pomysł, w jaki sposób mogę uruchomić spust, przerwać operację, nadal zwracając coś innego niż NULL?

+0

Więc próbujesz zbudować wyzwalacza w tabeli X, który wstawia dane do Y i Z, zapobiega niczego przed włożona X, ale zwraca jakiś identyfikator? –

+0

Tak, dzielę dane na odziedziczone tabele. Więc nie chcę duplikatu w tabeli nadrzędnej. Zasadniczo chcę, aby ten proces był całkowicie przejrzysty. Zajmujesz się tabelą nadrzędną, wyzwalacze robią wszystko za kulisami. Nic się nie zmienia z zewnątrz. –

Odpowiedz

5

Twoje pytanie pozostawia miejsce do interpretacji. Sposób, w jaki go rozumiem, chcesz, aby klauzula RETURNING komendy INSERT zwróciła wartość klucza podstawowego wygenerowanego przez sekwencję.

Istnieją inne sposoby, aby to osiągnąć. Podobnie jak przy użyciu nextval(), aby uzyskać następny numer id z sekwencji i wstawić wiersz z literą id.
OR currval()/lastval(), aby uzyskać ostatnio uzyskaną wartość dla sekwencji/dowolnej sekwencji w bieżącej sesji. Więcej w tej powiązanej odpowiedzi:
PostgreSQL next value of the sequences?

Do tego celu można również użyć RULE ... INSTEAD ...

Ale, aby odpowiedzieć na twoje pytanie - jeśli tak jest, twoje pytanie: można to zrobić, używając dwóch wyzwalaczy. Jeden BEFORE, jeden. Obie są uruchamiane w jednej transakcji z definicji, więc rząd fantomowy w pierwszej tabeli nigdy nie jest widoczny dla nikogo (z wyjątkiem wyzwalaczy).

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

połączeń w 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

Dzięki! To jest dokładnie to, czego chciałem. Moją obawą jest wydajność, dodanie wiersza i usunięcie go. Wiem, że to nie jest duży hit, po prostu wydaje się brudny. Nie można tego zrobić w żaden inny sposób? Dzięki za pomoc. –

+0

Jestem pewien, że istnieją inne sposoby, ale o to prosiliście. W mojej odpowiedzi zasugerowałem użycie nextval(). Widziałeś to? (Plus link.) Tak mogę to zrobić. Wadą jest 'nextval()': jeszcze jedno wywołanie na serwer. To zależy od okoliczności, które będą szybsze. Mój przykład jest obejściem, ale powinien być całkowicie bezpieczny w użyciu. –