Zkuste složený spouštěč:
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;
/
========UPRAVIT – několik otázek a odpovědí ===========
Otázka:Proč dochází k chybě mutující tabulky?
Odpověď:Toto je popsáno v dokumentaci:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
Otázka:jak se vyhnout chybě mutující tabulky?
Odpověď:Dokumentace doporučuje použití spouštěcího mechanismu, viz toto:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
Otázka:Co je to složený spouštěč a jak funguje?
Odpověď:Toto je rozsáhlé téma, podívejte se prosím na dokumentaci zde:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
Ve zkratce:jedná se o speciální druh spouštěče, který umožňuje kombinovat čtyři typy samostatných spouštěčů:BEFORE statement
, BEFORE-for each row
, AFTER for each row
a AFTER statament
do jednoho prohlášení. Usnadňuje implementaci některých scénářů, ve kterých je potřeba předávat některá data z jednoho spouštěče do jiného. Další podrobnosti naleznete na výše uvedeném odkazu.
O:Ale co vlastně znamená "Departments( :new.department ) := :new.department;
?
Odpověď:Tato deklarace ukládá číslo oddělení do asociativního pole.
Toto pole je deklarováno v deklarativní části složeného spouštěče:
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
Dokumentace týkající se složených spouštěčů říká, že:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Výše uvedené znamená, že Departments
proměnná se inicializuje pouze jednou na začátku celého zpracování, hned po spuštění spouště. "Trvání příkazu spouštění" znamená, že tato proměnná je po dokončení spouštěče zničena.
Tento příkaz:Departments( :new.department ) := :new.department;
ukládá číslo oddělení do asociativního pole. Je v BEFORE EACH ROW
sekce, pak se provede pro každý řádek, který je aktualizován (nebo vložen) příkazem update/insert.:new
a :old
jsou pseudozáznamy, více o nich najdete zde: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
Ve zkratce::new.department
načte novou hodnotu department
sloupec- pro aktuálně aktualizovaný řádek (aktualizovaná hodnota - PO aktualizaci), zatímco :old.department
poskytuje starou hodnotu tohoto sloupce (PŘED aktualizací).
Tato kolekce je později použita v AFTER STATEMENT
, když spouštěče vyberou všechna aktualizovaná oddělení (ve FOR-LOOP), pro každé oddělení se spustí SELECT SUM(salary) ...
a poté zkontroluje, zda je tento součet menší než 1000
Zvažte jednoduchou aktualizaci:UPDATE treballa SET salary = salary + 10
. Toto je jeden příkaz aktualizace, ale mění mnoho řádků najednou. Pořadí provádění našeho spouštěče je následující:
- Spustí se aktualizační prohlášení:
UPDATE treballa SET salary = salary + 10
- Je provedena deklarativní část spouštěče, to znamená:
Departments
proměnná je inicializována BEFORE EACH ROW
sekce se provádí samostatně pro každý aktualizovaný řádek - tolikrát, kolikrát je řádků k aktualizaci. Na tomto místě shromažďujeme všechna oddělení ze změněných řádků.AFTER STATEMENT
sekce je provedena. V tuto chvíli je již tabulka aktualizována – všechny řádky již mají nové, aktualizované platy. Procházíme odděleními uloženými vDepartments
a u každého zkontrolujeme, zda je součet platů menší nebo roven 1000. Pokud je tento součet> 1000 pro některé z těchto oddělení, dojde k chybě a celá aktualizace se přeruší a vrátí zpět. V opačném případě spoušť skončí a aktualizace je dokončena (ale tyto změny musíte stejně provést).
O:Co je to asociativní pole a proč se používá právě tento druh kolekce, a nikoli jiné kolekce (varray nebo vnořená tabulka)?
A:Kolekce PL/SQL jsou obrovské téma. Chcete-li se je naučit, klikněte na tento odkaz:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
Ve zkratce – Asociativní pole (nebo index podle tabulky) je jako mapa v jazyce Java (hashmap, treemap atd.) – je to sada párů klíč–hodnota a každý klíč je unikátní . Do tohoto pole můžete vložit stejný klíč mnohokrát (s různými hodnotami), ale tento klíč bude uložen pouze jednou - je jedinečný.
Použil jsem ho k získání jedinečné sady oddělení.
Znovu zvažte náš příklad aktualizace:UPDATE treballa SET salary = salary + 10
- tento příkaz se dotkne stovek řádků, které mají stejné oddělení. Nechci kolekci se stejným oddělením duplikovaným 100krát, potřebuji jedinečnou sadu oddělení a chci provést náš dotaz SELECT sum()...
pouze jednou pro každé oddělení, nikoli 100krát. S pomocí sssociativního pole se to děje automaticky – získám jedinečnou sadu oddělení.