Probeer een samengestelde trigger:
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;
/
========BEWERKEN - een paar vragen en antwoorden ===========
V:Waarom treedt er een muterende tabelfout op?
A:Dit wordt beschreven in de documentatie:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
V:hoe kan ik een muterende tabelfout vermijden?
A:De documentatie beveelt het gebruik van een samengestelde trigger aan, zie deze:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
V:Wat is een samengestelde trigger en hoe werkt het?
A:Dit is een enorm onderwerp, raadpleeg de documentatie hier:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
Kortom:dit is een speciaal soort trigger die het mogelijk maakt om vier soorten afzonderlijke triggers te combineren:BEFORE statement
, BEFORE-for each row
, AFTER for each row
en AFTER statament
in één aangifte. Het maakt het gemakkelijker om een aantal scenario's te implementeren waarin het nodig is om bepaalde gegevens van de ene trigger naar de andere door te geven. Bekijk de bovenstaande link voor meer details.
V:Maar wat doet "Departments( :new.department ) := :new.department;
eigenlijk? ?
A:Deze declaratie slaat een afdelingsnummer op in een associatieve array.
Deze array wordt gedeclareerd in een declaratief deel van de samengestelde trigger:
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
De documentatie met betrekking tot de samengestelde triggers zegt dat:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Het bovenstaande betekent dat Departments
variabele wordt slechts één keer geïnitialiseerd aan het begin van de hele verwerking, net nadat de trigger is geactiveerd. "Firing-statement duration" betekent dat deze variabele wordt vernietigd nadat de trigger is voltooid.
Deze instructie:Departments( :new.department ) := :new.department;
slaat een afdelingsnummer op in de associatieve array. Het staat in BEFORE EACH ROW
sectie, dan wordt het uitgevoerd voor elke rij die is bijgewerkt (of ingevoegd) door de update/insert-instructie.:new
en :old
zijn pseudorecords, meer hierover kun je hier vinden: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
Kortom::new.department
haalt een nieuwe waarde op van department
kolom- voor een momenteel bijgewerkte rij (bijgewerkte waarde - NA de update), terwijl :old.department
geeft een oude waarde van deze kolom (VOOR de update).
Deze verzameling wordt later gebruikt in de AFTER STATEMENT
, wanneer de triggers alle bijgewerkte afdelingen kiezen (in een FOR-LOOP), vuren voor elke afdeling SELECT SUM(salary) ...
en controleert dan of dit bedrag kleiner is dan 1000
Overweeg een simpele update:UPDATE treballa SET salary = salary + 10
. Dit is een enkele update-instructie, maar verandert veel rijen tegelijk. De volgorde van uitvoering van onze trigger is als volgt:
- De update-verklaring is geactiveerd:
UPDATE treballa SET salary = salary + 10
- Het declaratieve gedeelte van de trigger wordt uitgevoerd, dat wil zeggen:
Departments
variabele is geïnitialiseerd BEFORE EACH ROW
sectie wordt uitgevoerd, afzonderlijk voor elke bijgewerkte rij - zo vaak als er rijen zijn die moeten worden bijgewerkt. Op deze plaats verzamelen we alle afdelingen van gewijzigde rijen.AFTER STATEMENT
sectie wordt uitgevoerd. Op dit punt is de tabel al bijgewerkt - alle rijen hebben al nieuwe, bijgewerkte salarissen. We doorlopen afdelingen die zijn opgeslagen inDepartments
en voor elk controleren we of de som van de salarissen kleiner of gelijk is aan 1000. Als deze som> 1000 is voor een van deze afdelingen, wordt er een fout gegenereerd en wordt de hele update afgebroken en teruggedraaid. Anders wordt de trigger voltooid en is de update voltooid (maar u moet deze wijzigingen toch vastleggen).
V:Wat is een associatieve array en waarom wordt juist dit soort verzameling gebruikt in plaats van andere verzamelingen (een varray of een geneste tabel)?
A:PL/SQL-collecties zijn een groot onderwerp. Volg deze link om ze te leren:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
Kortom - Associatieve array (of index-by-tabel) is als een map in Java (hashmap, treemap, enz.) - het is een set sleutel-waardeparen en elke sleutel is uniek . Je kunt dezelfde sleutel meerdere keren in deze array plaatsen (met verschillende waarden), maar deze sleutel wordt maar één keer opgeslagen - het is uniek.
Ik heb het gebruikt om een unieke set afdelingen te krijgen.
Bekijk ons updatevoorbeeld nog eens:UPDATE treballa SET salary = salary + 10
- dit commando raakt honderden rijen die dezelfde afdeling hebben. Ik wil niet dat een verzameling met dezelfde afdeling 100 keer wordt gedupliceerd, ik heb een unieke set afdelingen nodig en ik wil onze query uitvoeren SELECT sum()...
slechts één keer voor elke afdeling, niet 100 keer. Met behulp van de sssociative array wordt dit automatisch gedaan - ik krijg een unieke set afdelingen.