Wypróbuj wyzwalacz złożony:
CREATE OR REPLACE TRIGGER compound_trigger_name
FOR INSERT OR UPDATE OF salary ON treballa
COMPOUND TRIGGER
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
BEFORE EACH ROW IS
BEGIN
-- collect updated or inserted departments
Departments( :new.department ) := :new.department;
END BEFORE EACH ROW;
AFTER STATEMENT IS
sum_sal NUMBER;
BEGIN
-- for each updated department check the restriction
FOR dept IN Departments.FIRST .. Departments.LAST
LOOP
SELECT sum(salary) INTO sum_sal FROM treballa WHERE department = dept;
IF sum_sal > 1000 THEN
raise_application_error(-20123, 'The total salary for department '||dept||' cannot exceed 1000');
END IF;
END LOOP;
END AFTER STATEMENT;
END compound_trigger_name;
/
========EDIT - kilka pytań i odpowiedzi ===========
P:Dlaczego występuje błąd tabeli mutacji?
O:Jest to opisane w dokumentacji:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
P:jak uniknąć błędu tabeli mutacji?
O:Dokumentacja zaleca użycie wyzwalacza złożonego, zobacz to:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
P:Co to jest wyzwalacz złożony i jak działa?
O:To obszerny temat, zapoznaj się z dokumentacją tutaj:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
W skrócie:jest to specjalny rodzaj wyzwalacza, który umożliwia połączenie czterech typów oddzielnych wyzwalaczy:BEFORE statement
, BEFORE-for each row
, AFTER for each row
i AFTER statament
w jedną deklarację. Ułatwia realizację scenariuszy, w których zachodzi potrzeba przekazywania danych z jednego wyzwalacza do drugiego. Zapoznaj się z powyższym linkiem, aby uzyskać więcej informacji.
P:Ale co właściwie oznacza "Departments( :new.department ) := :new.department;
?
O:Ta deklaracja przechowuje numer działu w tablicy asocjacyjnej.
Ta tablica jest zadeklarowana w deklaratywnej części wyzwalacza złożonego:
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
Dokumentacja związana ze złożonymi wyzwalaczami mówi, że:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Powyższe oznacza, że Departments
zmienna jest inicjowana tylko raz na początku całego przetwarzania, zaraz po odpaleniu wyzwalacza. „Czas trwania instrukcji uruchamiającej” oznacza, że zmienna ta jest niszczona po zakończeniu wyzwalacza.
Ta instrukcja:Departments( :new.department ) := :new.department;
przechowuje numer działu w tablicy asocjacyjnej. Znajduje się w BEFORE EACH ROW
sekcji, to jest wykonywane dla każdego wiersza, który jest aktualizowany (lub wstawiany) przez instrukcję update/insert.:new
i :old
to pseudorekordy, więcej na ich temat znajdziesz tutaj: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
W skrócie::new.department
pobiera nową wartość department
kolumna- dla aktualnie zaktualizowanego wiersza (zaktualizowana wartość - PO aktualizacji), natomiast :old.department
podaje starą wartość tej kolumny (PRZED aktualizacją).
Ta kolekcja jest później używana w AFTER STATEMENT
, gdy wyzwalacze wybierają wszystkie zaktualizowane działy (w FOR-LOOP), dla każdego działu uruchamia SELECT SUM(salary) ...
a następnie sprawdza, czy suma ta jest mniejsza niż 1000
Rozważ prostą aktualizację:UPDATE treballa SET salary = salary + 10
. To jest pojedyncza instrukcja aktualizacji, ale zmienia wiele wierszy naraz. Kolejność wykonania naszego wyzwalacza jest następująca:
- Uruchomiony komunikat aktualizacji:
UPDATE treballa SET salary = salary + 10
- Wykonywana jest deklaratywna sekcja wyzwalacza, czyli:
Departments
zmienna jest inicjowana BEFORE EACH ROW
sekcja jest wykonywana osobno dla każdego zaktualizowanego wiersza - tyle razy, ile jest wierszy do zaktualizowania. W tym miejscu zbieramy wszystkie działy ze zmienionych rzędów.AFTER STATEMENT
sekcja jest wykonywana. W tym momencie tabela jest już zaktualizowana - wszystkie wiersze mają już nowe, zaktualizowane pensje. Przechodzimy przez działy zapisane wDepartments
i dla każdego sprawdzamy, czy suma wynagrodzeń jest mniejsza lub równa 1000. Jeśli ta suma jest> 1000 dla któregoś z tych działów, to wyrzucany jest błąd, a cała aktualizacja jest przerywana i cofana. W przeciwnym razie wyzwalacz zakończy działanie, a aktualizacja zostanie zakończona (ale i tak musisz zatwierdzić te zmiany).
P:Co to jest tablica asocjacyjna i dlaczego używany jest właśnie ten rodzaj kolekcji, a nie inne kolekcje (tablica zmienna lub tabela zagnieżdżona)?
O:Kolekcje PL/SQL to ogromny temat. Kliknij ten link, aby się ich nauczyć:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
W skrócie - tablica asocjacyjna (lub tabela indeksowana według indeksu) jest jak mapa w Javie (hashmap, treemap itp.) - jest to zestaw par klucz-wartość, a każdy klucz jest unikalny . Możesz umieścić ten sam klucz wiele razy w tej tablicy (o różnych wartościach), ale ten klucz będzie przechowywany tylko raz - jest unikalny.
Użyłem go do uzyskania unikalnego zestawu działów.
Rozważ ponownie nasz przykład aktualizacji:UPDATE treballa SET salary = salary + 10
- to polecenie dotyka setek wierszy, które mają ten sam dział. Nie chcę, aby kolekcja z tym samym działem została zduplikowana 100 razy, potrzebuję unikalnego zestawu działów i chcę wykonać nasze zapytanie SELECT sum()...
tylko raz na każdy dział, a nie 100 razy. Z pomocą tablicy skojarzeniowej odbywa się to automatycznie - otrzymuję unikalny zestaw wydziałów.