Wyzwalacze
Wyzwalacze są to bloki PL/SQL, których wywołanie następuje automatycznie w przypadku zajścia jakiegoś zdarzenia.
Definiując wyzwalacz określamy, w jakim przypadku ma się on uruchomić. W wersji Oracle poniżej 8i można było zdefiniować tylko wyzwalacze powiązane z operacjami DML na tabelach. W wersji 8i możliwe jest powiązanie wyzwalaczy z operacjami DML na widokach oraz ze zdarzeniami systemowymi.
Wyzwalacze DML
Tworzenie, usuwanie
CRAETE [OR REPLACE] TRIGGER nazwa_wyzwalacza
{BEFORE|AFTER}
{INSERT|UPDATE|DELERE|INSERT OR UPDATE OR DELETE}
ON nazwa_tabeli
[FOR EACH ROW [WHEN warunek]]
ciało_wyazwalacza;
DROP TRIGGER nazwa_wyzwalacza;
ALTER TRIGGER nazwa_wyzwalacza {DISABLE|ENABLE};
Kolejność wykonywania się wyzwalaczy DML
Jeżeli na pewnej tabeli są założone wszystkie rodzaje wyzwalaczy, to ich kolejność jest ściśle określona.
Przykład 1
-- zostaje wykonana instrukcja:
UPDATE tabela1
SET kol1 = wartość1
WHERE kol1 = wartość2;
-- Instrukcja ta spowodowała modyfikację 3 wierszy w tabela1
-- Kolejność wykonywania wyzwalaczy jest następujące
BEFORE UPDATE ON tabela1
BEFORE UPDATE ON tabela1 FOR EACH ROW
-- modyfikacja jednego wiersza
AFTER UPDATE ON tabela1 FOR EACH ROW
BEFORE UPDATE ON tabela1 FOR EACH ROW
-- modyfikacja jednego wiersza
AFTER UPDATE ON tabela1 FOR EACH ROW
BEFORE UPDATE ON tabela1 FOR EACH ROW
-- modyfikacja jednego wiersza
AFTER UPDATE ON tabela1 FOR EACH ROW
AFTER UPDATE ON tabela1
Pseudorekordy dostępne w wyzwalaczach
W każdym wyzwalaczu FOR EACH ROW dostępne są dwie zmienne rekordowe, które zawierają informacje o wierszu, który aktualnie jest przetwarzany.
Zmienne te nazywają się :old i :new i zawierają odpowiednio w zależności od typu wyzwalacza wiersz usuwany, dodawany, bądź modyfikowany.
Zmienne rekordowe mogą być wykorzystywane nie tylko do odczytu, ale w wyzwalaczach BEFORE INSERT i BEFORE UPDATE mogą być również modyfikowane, co skutkuje wstawieniem do tabeli odpowiednio zmodyfikowanych danych.
Jeżeli w wyzwalaczach DML zostanie wywołany błąd, to operacja, która spowodowała wyzwolenie tego wyzwalacza zakończy się niepowodzeniem i zgłosi błąd, który został wywołany w wyzwalaczu.
Predykaty
Jeden wyzwalacz może być powiązany z wszystkimi operacjami: INSERT, UPDATE i DELETE.
Dzięki predykatom wewnątrz kodu wyzwalacza można rozpoznać, jaka operacja spowodowała uruchomienie wyzwalacza.
Istnieją trzy predykaty: INSERTING, UPDATING i DELETING i przybierają one odpowiednio wartość TRUE bądź FALSE w zależności od tego, jaka operacja spowodowała uruchomienie wyzwalacza.
-- Obsługa modyfikacji pozycji zamówień
CREATE OR REPLACE TRIGGER tg_pzm_modif
AFTER UPDATE OR DELETE OR INSERT ON pozycje_zamowien
FOR EACH ROW
DECLARE
v_dummy number(1);
v_cena towary.tow_cena%TYPE;
BEGIN
-- Czy modyfikacja nie dotyczy zamówienia zrealizowanego?
BEGIN
SELECT 1 INTO v_dummy
FROM zamowienia
WHERE zam_data_realizacji IS NOT NULL
AND (zam_id = :old.pzm_zam_id
OR zam_id = :new.pzm_zam_id);
RAISE too_many_rows;
EXCEPTION
WHEN no_data_found THEN
NULL;
WHEN too_many_rows THEN
raise_application_error(-20002,
'Nie można modyfikować zrealizowanych zamówień');
END;
-- Odpowiednie zmodyfikowanie kwoty zamówienia
IF INSERTING THEN –- dodanie nowej pozycji do zamówienia
SELECT tow_cena INTO v_cena
FROM towary
WHERE tow_id = :new.pzm_tow_id;
UPDATE zamowienia
SET zam_kwota = zam_kwota + (v_cena * :new.pzm_ilosc)
WHERE zam_id = :new.pzm_zam_id;
ELSIF DELETING THEN -– usunięcie pozycji z zamówienia
SELECT tow_cena INTO v_cena
FROM towary
WHERE tow_id = :old.pzm_tow_id;
UPDATE zamowienia
SET zam_kwota = zam_kwota - (v_cena * :old.pzm_ilosc)
WHERE zam_id = :old.pzm_zam_id;
ELSE -- modyfikacja pozycji zamówienia
SELECT tow_cena INTO v_cena
FROM towary
WHERE tow_id = :new.pzm_tow_id;
UPDATE zamowienia
SET zam_kwota = zam_kwota + (v_cena * :new.pzm_ilosc)
WHERE zam_id = :new.pzm_zam_id;
SELECT tow_cena INTO v_cena
FROM towary
WHERE tow_id = :old.pzm_tow_id;
UPDATE zamowienia
SET zam_kwota = zam_kwota - (v_cena * :old.pzm_ilosc)
WHERE zam_id = :old.pzm_zam_id;
END IF;
END;
Predykaty
Wyzwalacze te mogą być powiązane tylko z perspektywami i wykonują się nie przed operacją DML, nie po operacji DML, ale zamiast operacji DML, i służą do tego, aby interpretować odpowiednio operacje na perspektywach, na których dotychczas nie było wolno wykonywać operacji DML.
CRAETE [OR REPLACE] TRIGGER nazwa_wyzwalacza
INSTEAD OF {INSERT|UPDATE|DELETE|INSERT OR UPDATE OR DELETE}
ON nazwa_widoku
[WHEN warunek]
ciało_wyazwalacza;
Klauzula FOR EACH ROW jest opcjonalna i nic nie wnosi, ponieważ wszystkie wyzwalacze INSTEAD OF są wyzwalaczami na poziomie wiersza.
W wyzwalaczach INSTEAD OF dostępne są także zmienne rekordowe :old i :new oraz predykaty INSERTING, UPDATING i DELETING.
Przykład
Stworzyć perspektywę obejmującą identyfikator, imię, nazwisko lub nazwę klienta oraz identyfikatory i opisy wszystkich zamówień składanych przez tych klientów. Następnie stworzyć wyzwalacz, który obsłuży operację usuwania rekordów ze stworzonej perspektywy. Wyzwalacz powinien:• usunąć wszystkie pozycje usuwanego zamówienia,
• usunąć zamówienie,
• jeżeli było to jedyne zamówienie danego klienta, to powinien zostać usunięty także klient.
-- stworzenie widoku
CREATE OR REPLACE VIEW kli_zam AS
SELECT K.klt_id id_kl,
decode(K.klt_typ,'IND',K.klt_nazwisko||' '||K.klt_imie,
K.klt_nazwa) klient,
Z.zam_opis opis,
Z.zam_id id_zam
FROM klienci K, zamowienia Z
WHERE K.klt_id = Z.zam_klt_id;
-- stworzenie odpowiedniego wyzwalacza
CREATE OR REPLACE TRIGGER tg_kli_zam_del
INSTEAD OF DELETE ON kli_zam
DECLARE
v_ile binary_integer;
BEGIN
-- usunięcie pozycji usuwanego zamowienia
DELETE pozycje_zamowien
WHERE pzm_zam_id = :old.id_zam;
-- usunięcie zamówienia
DELETE zamowienia
WHERE zam_id = :old.id_zam;
-- sprawdzenie, czy klienta ma jeszcze jakieś zamówienia
SELECT count(*) INTO v_ile
FROM zamowienia
WHERE zam_klt_id = :old.id_kl;
-- jeżeli nie ma więcej zamówień, to usunięcie klienta
IF v_ile = 0 THEN
DELETE klienci
WHERE klt_id = :old.id_kl;
END IF;
END;
Wyzwalacze DDL
Wyzwalacze systemowe mogą być powiązane z operacjami DDL takimi jak CREATE, ALTER i DROP, oraz ze zdarzeniami systemowymi, takimi jak startup/shutdown serwer, logon/logof użytkownik i server error.
CREATE [OR REPLACE] TRIGGER [schemat.]nazwa
{BEFORE|AFTER}
{lista_zdarzeń_ddl|lista_zdarzeń_systemowych}
ON {DATABASE | SCHEMA}
[WHEN warunek]
ciało_wyzwalacza
Nie dla wszystkich zdarzeń systemowych można założyć wyzwalacze BEFORE i AFTER.
Wyzwalacze systemowe mogą być definiowane na poziomie pojedynczego schematu lub całej bazy. Wyjątkiem jest startup i shutdown, które mogą być definiowane tylko na poziomie bazy.
W przypadku wyzwalaczy systemowych istnieje kilka funkcji pozwalających otrzymać informacje na temat zdarzenia, które zainicjowało uruchomienie wyzwalacza (analogiczne do :new, :old w wyzwalaczach DML i INSTEAD OF).
Przykład
Stworzyć wyzwalacz, który będzie odnotowywał w specjalnej tabeliddl_log informacje o wszystkich tworzonych obiektach na schemacie, na którym będzie stworzony wyzwalacz.
-- logowanie wszystkich tworzonych obiektów na schemacie
CREATE OR REPLACE TRIGGER tg1
AFTER CREATE ON SCHEMA
BEGIN
INSERT INTO ddl_log (user_id
, obj_type
, obj_name
, obj_owner
, cre_time)
VALUES (USER
, SYS.DICTIONARY_OBJ_TYPE
, SYS.DICTIONARY_OBJ_NAME
, SYS.DICTIONARY_OBJ_OWNER
, SYSDATE);
END;
Ograniczenia wyzwalaczy
Tabele modyfikowane
Istnieją pewne ograniczenia w wyzwalaczach dotyczące dostępu do tabel. Ważne jest aby rozróżnić pewne typy tabel.
Tabele modyfikowane (mutating tables) to te tabele, które są modyfikowane przez instrukcję DML, która spowodowała uruchomienie wyzwalacza. Są to także tabele podrzędne przyłączone do tabeli modyfikowanej kluczem obcym z opcją CASCADE DELETE.
Tabele ograniczone (constraints tables) to tabele, które muszą być odczytywane podczas modyfikowania danych w tabeli modyfikowanej. Praktycznie są to tabele powiązane kluczem obcym z tabelami modyfikowanymi.
- Żadne polecenie SQL w ciele wyzwalacza nie może czytać ani modyfikować tabeli modyfikowanej, zwłaszcza tabeli, na której jest wyzwalacz oparty.
-
Żadne polecenie SQL w ciele wyzwalacza nie może czytać ani modyfikować kluczy unikalnych, głównych i obcych tabel ograniczonych. Możliwe jest modyfikowanie pozostałych kolumn.
Ograniczenia powyższe dotyczą wszystkich wyzwalaczy na poziomie wierszy (FOR EACH ROW). Jeżeli PolecenieINSERTdotyczy tylko jednego wiersza, to w ciele wyzwalaczaBEFOREiAFTERwyjątkowo można się odwoływać do tabeli, na której jest oparty wyzwalacz.
Transakcje
Ciało wyzwalaczy nie może zawierać żadnych poleceń związanych z transakcjami. Co za tym idzie, nie może wywoływać pośrednio podprogramów, które zawierają takie polecenia bezpośrednio lub pośrednio.
