[plpgsql] Triger before/after - Problem ze zrozumieniem.
kraf101 - 28-09-2007 00:11
[plpgsql] Triger before/after - Problem ze zrozumieniem.
Witam,
Robię bazkę w PGSQL i mam pewien problem ze zrozumieniem na czym polega różnica w deklaracjach triggerów before/after insert/update/delete.
Powiedzmy, że mamy dwie tabele "faktury" i "wpłaty" w relacji "1 do wiele" (model bazy poniżej). Pole "saldo" jest wyliczane przez trigger na funkcji faktury_i_u() (before insert/update). Pole "zaplacono" w tablicy faktury jest wypełniane przez trigger na funkcji wplaty_i_u() oraz wplaty_d() (delete).
Nie bardzo rozumiem jaka jest różnica między deklaracjami before a after. W przypadku naruszenia ograniczenia pola "saldo > = 0" operacja insert/update jest odrzucana zarówno dla deklaracji triggera "before" jak i "after".
Jeszcze dziwniej jest przy usuwaniu wiersza z tablicy "wplaty". W przypadku deklaracji "before", triggery wplaty_d() i faktury_i_u() są odpalone i wykonują zmiany na tablicy "faktury" ale wiersz NIE ZOSTAJE USUNIĘTY - bez żadnego komunikatu do klienta. W psql podaje "DELETE 0". W przypadku deklaracji "after" wszystko wygląda OK, wykonują się zarówno triggery jak i "DELETE 1"
Dlaczego tak jest ???
Dzięki RAF
CREATE TABLE faktury ( fak_id serial NOT NULL, do_zaplaty float8 NOT NULL, zaplacono float8 NOT NULL DEFAULT 0, saldo float8 NOT NULL DEFAULT 0, CONSTRAINT fak_pkey PRIMARY KEY (fak_id), CONSTRAINT saldo_no_minus CHECK (saldo >= 0::double precision), CONSTRAINT zap_no_minus CHECK (zaplacono >= 0::double precision) );
CREATE OR REPLACE FUNCTION faktury_i_u() RETURNS "trigger" AS $BODY$BEGIN NEW.saldo := NEW.do_zaplaty - NEW.zaplacono; RETURN NEW; END;$BODY$ LANGUAGE 'plpgsql' VOLATILE;
CREATE TRIGGER i_u BEFORE INSERT OR UPDATE ON faktury FOR EACH ROW EXECUTE PROCEDURE faktury_i_u();
CREATE TABLE wplaty ( wpl_id serial NOT NULL, fak_id int4 NOT NULL, kwota float8 NOT NULL, CONSTRAINT wpl_pkey PRIMARY KEY (wpl_id), CONSTRAINT fak_ref FOREIGN KEY (fak_id) REFERENCES faktury (fak_id) MATCH SIMPLE ON UPDATE RESTRICT ON DELETE RESTRICT );
CREATE OR REPLACE FUNCTION wplaty_i_u() RETURNS "trigger" AS $BODY$DECLARE wpl_pop FLOAT8; zap_fak FLOAT8;
BEGIN
IF TG_OP = 'INSERT' THEN wpl_pop := 0; ELSE wpl_pop := OLD.kwota; END IF;
SELECT zaplacono INTO zap_fak FROM faktury WHERE fak_id = NEW.fak_id; UPDATE faktury SET zaplacono = zap_fak + NEW.kwota - wpl_pop WHERE fak_id = NEW.fak_id;
RETURN NEW; END;$BODY$ LANGUAGE 'plpgsql' VOLATILE;
CREATE TRIGGER i_u AFTER INSERT OR UPDATE ON wplaty FOR EACH ROW EXECUTE PROCEDURE wplaty_i_u();
CREATE OR REPLACE FUNCTION wplaty_d() RETURNS "trigger" AS $BODY$DECLARE zap_fak FLOAT8;
BEGIN
SELECT zaplacono INTO zap_fak FROM faktury WHERE fak_id = OLD.fak_id; UPDATE faktury SET zaplacono = zap_fak - OLD.kwota WHERE fak_id = OLD.fak_id;
RETURN NEW; END;$BODY$ LANGUAGE 'plpgsql' VOLATILE;
CREATE TRIGGER d BEFORE DELETE ON wplaty FOR EACH ROW EXECUTE PROCEDURE wplaty_d();
=?ISO-8859-2?Q?Pawe=B3_Matejski?= - 28-09-2007 00:11
kraf101 wrote: > Witam, > > Robię bazkę w PGSQL i mam pewien problem ze zrozumieniem na czym polega > różnica w deklaracjach triggerów before/after insert/update/delete. > > Powiedzmy, że mamy dwie tabele "faktury" i "wpłaty" w relacji "1 do wiele" > (model bazy poniżej). > Pole "saldo" jest wyliczane przez trigger na funkcji faktury_i_u() (before > insert/update). Pole "zaplacono" w tablicy faktury jest wypełniane przez > trigger na funkcji wplaty_i_u() oraz wplaty_d() (delete). > > Nie bardzo rozumiem jaka jest różnica między deklaracjami before a after. > W przypadku naruszenia ograniczenia pola "saldo > = 0" operacja > insert/update jest odrzucana zarówno dla deklaracji triggera "before" jak > i "after".
Ale w AFTER nie możesz zrobić tego po ciuchu, przez zwrócenie NULL. Nie możesz też zmieniać danych w NEW rekordu.
> Jeszcze dziwniej jest przy usuwaniu wiersza z tablicy "wplaty". > W przypadku deklaracji "before", triggery wplaty_d() i faktury_i_u() są > odpalone i wykonują zmiany na tablicy "faktury" ale wiersz NIE ZOSTAJE > USUNIĘTY - bez żadnego komunikatu do klienta. W psql podaje "DELETE 0". > W przypadku deklaracji "after" wszystko wygląda OK, wykonują się zarówno > triggery jak i "DELETE 1" > > Dlaczego tak jest ??? > > CREATE OR REPLACE FUNCTION wplaty_d() > RETURNS "trigger" AS > $BODY$DECLARE > zap_fak FLOAT8; > > BEGIN > > SELECT zaplacono INTO zap_fak FROM faktury WHERE fak_id = OLD.fak_id; > UPDATE faktury SET zaplacono = zap_fak - OLD.kwota WHERE fak_id = > OLD.fak_id; > > RETURN NEW;
Zastanów się, jaką wartość może mieć NEW w DELETE. I co się stanie, jak tę wartość zwrócisz w BEFOR. :)
> END;$BODY$ > LANGUAGE 'plpgsql' VOLATILE;
-- P.M.
kraf101 - 28-09-2007 00:11
Paweł Matejski wrote:
> > Ale w AFTER nie możesz zrobić tego po ciuchu, przez zwrócenie NULL. Nie > możesz też zmieniać danych w NEW rekordu. > > > Zastanów się, jaką wartość może mieć NEW w DELETE. I co się stanie, jak tę > wartość zwrócisz w BEFOR. :) >
Dzięki,
Trochę załapałem, ale dalej to jakieś porąbane ;) Jest gdzieś może jakieś info kiedy i co się powinno używać? Tak żeby było poprawnie i nie wpadać w jakieś pułapki?
Pozdrawiam RAF
=?ISO-8859-2?Q?Pawe=B3_Matejski?= - 28-09-2007 00:11
kraf101 wrote: > Paweł Matejski wrote: > > >> Ale w AFTER nie możesz zrobić tego po ciuchu, przez zwrócenie NULL. Nie >> możesz też zmieniać danych w NEW rekordu. >> >> >> Zastanów się, jaką wartość może mieć NEW w DELETE. I co się stanie, jak tę >> wartość zwrócisz w BEFOR. :) >> > > Dzięki, > > Trochę załapałem, ale dalej to jakieś porąbane ;) > Jest gdzieś może jakieś info kiedy i co się powinno używać? > Tak żeby było poprawnie i nie wpadać w jakieś pułapki?
W dokumentacji postgresa jest rozdział triggers.
Ale zasady są proste. W BEFOR robisz wszelkie zmiany na rekordzie wyzwalającym trigger - bo gdzie indziej się nie da, oraz walidację - żeby zaoszczędzić pracy pozostałym triggerom albo wycofać się ze zmian "po cichu", bez przerywania transakcji
W AFTER robisz updaty pozostałych tabel - jesteś już pewny wprowadzonych danych, bo już ich nie można zmieniać.
-- P.M.
zanotowane.pldoc.pisz.plpdf.pisz.pleffulla.pev.pl
|
[MSSQL2000] Problem z =?ISO-8859-2?Q?tabel=B1/indeksem/zapytanie?==?ISO-8859-2?Q?m_czy_b=B3=B1d_w_bazie_danych=2E=2E=2E?=
mysql i mysql-front, problem
String line; if (line=="cos"){...}....problem
Problemy z =?ISO-8859-2?Q?instalacj=B1_PostgreSQL_na_syste?==?ISO-8859-2?Q?mach_Windows?=
[postgres] Problem z =?ISO-8859-2?Q?zmian=B1_struktury_i_z?==?ISO-8859-2?Q?ale=BFno=B6ciami=2E?=
[oracle] =?ISO-8859-2?Q?zmia=BFd=BFony_przez_problem=3A_za?==?ISO-8859-2?Q?pytanie_do_hierarchi?=
Problem z wartościami w MySQL :( [ MySQL and ASP and VBScript ]
[PGSQL] czy ktos mial problemy z initdb pgsql 8.1 ?
[MySQL] Problem z zapisem danych w bazie danych
Problem z mysql - can't connect to MySQL/nietypowo...
zanotowane.pldoc.pisz.plpdf.pisz.plmisida.pev.pl
Cytat
Decede mihi sole - nie zasłaniaj mi słonca. Gdy kogoś kochasz, jesteś jak stworzyciel świata - na cokolwiek spojrzysz, nabiera to kształtu, wypełnia się barwą, światłem. Powietrze przytula się do ciebie, choćby był mróz, a ty masz w sobie tyle radości, że musisz ją rozdawać wokoło, bo się w tobie nie mieści Hoc fac - tak czyń. A tergo - od tyłu; z tyłu. I czarne włosy posiwieją. Safona |
|