Prøv en sammensat 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;
/
========REDIGER - et par spørgsmål og svar ===========
Sp:Hvorfor opstår der en mutationstabelfejl?
A:Dette er beskrevet i dokumentationen:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
Sp:hvordan undgår man en mutationstabelfejl?
A:Dokumentationen anbefaler brugen af en coumpound-trigger, se denne:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
Sp:Hvad er en sammensat trigger, og hvordan virker den?
A:Dette er et stort emne, se venligst dokumentationen her:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
Kort sagt:dette er en særlig slags trigger, der gør det muligt at kombinere fire typer separate triggere:BEFORE statement
, BEFORE-for each row
, AFTER for each row
og AFTER statament
til en én-erklæring. Det gør det lettere at implementere nogle scenarier, hvor der er behov for at overføre nogle data fra en trigger til en anden. Læs venligst ovenstående link for flere detaljer.
Sp:Men hvad gør "Departments( :new.department ) := :new.department;
?
A:Denne erklæring gemmer et afdelingsnummer i et associativt array.
Dette array er deklareret i en deklarativ del af den sammensatte trigger:
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
Dokumentationen relateret til de sammensatte triggere siger, at:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Ovenstående betyder, at Departments
variabel initialiseres kun én gang i begyndelsen af hele behandlingen, lige efter at triggeren udløses. "Udgivelsesudsagns varighed" betyder, at denne variabel ødelægges, efter at triggeren er færdig.
Denne erklæring:Departments( :new.department ) := :new.department;
gemmer et afdelingsnummer i det associative array. Det er i BEFORE EACH ROW
sektion, så udføres den for hver række, der opdateres (eller indsættes) af update/insert-sætningen.:new
og :old
er pseudorecords, mere om dem kan du finde her: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
Kort sagt::new.department
henter en ny værdi for department
kolonne- for en aktuelt opdateret række (opdateret værdi - EFTER opdateringen), mens :old.department
giver en gammel værdi af denne kolonne (FØR opdateringen).
Denne samling bruges senere i AFTER STATEMENT
, når triggerne vælger alle opdaterede afdelinger (i en FOR-LOOP), for hver afdeling fyrer SELECT SUM(salary) ...
og kontrollerer derefter, om denne sum er mindre end 1000
Overvej en simpel opdatering:UPDATE treballa SET salary = salary + 10
. Dette er en enkelt opdateringserklæring, men ændrer mange rækker på én gang. Rækkefølgen for udførelse af vores trigger er som følger:
- Opdateringsstatementet udløses:
UPDATE treballa SET salary = salary + 10
- Den deklarative sektion af triggeren udføres, dvs.:
Departments
variabel initialiseres BEFORE EACH ROW
sektion udføres, separat for hver opdateret række - så mange gange som der er rækker, der skal opdateres. På dette sted samler vi alle afdelinger fra ændrede rækker.AFTER STATEMENT
afsnit udføres. På dette tidspunkt er tabellen allerede opdateret - alle rækker har allerede nye, opdaterede lønninger. Vi gennemgår afdelinger gemt iDepartments
og for hver enkelt tjekker vi, om summen af lønninger er mindre eller lig med 1000. Hvis denne sum er> 1000 for nogen af disse afdelinger, bliver der smidt en fejl, og hele opdateringen afbrydes og rulles tilbage. Ellers afsluttes triggeren, og opdateringen er færdig (men du skal alligevel foretage disse ændringer).
Sp:Hvad er et associativt array, og hvorfor bruges netop denne form for samling i stedet for andre samlinger (en varray eller en indlejret tabel)?
Sv:PL/SQL-samlinger er et stort emne. Følg dette link for at lære dem at kende:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
Kort sagt - Associativ array (eller indeks-efter tabel) er som et kort i java (hashmap, trækort osv.) - det er et sæt nøgleværdi-par, og hver nøgle er unik . Du kan sætte den samme nøgle mange gange til dette array (med forskellige værdier), men denne nøgle vil kun blive gemt én gang - den er unik.
Jeg har brugt den til at få et unikt sæt af afdelinger.
Overvej vores opdateringseksempel igen:UPDATE treballa SET salary = salary + 10
- denne kommando berører hundredvis af rækker, der har samme afdeling. Jeg vil ikke have en samling med den samme afdeling duplikeret 100 gange, jeg har brug for et unikt sæt afdelinger, og jeg vil udføre vores forespørgsel SELECT sum()...
kun én gang for hver afdeling, ikke 100 gange. Ved hjælp af det sssociative array sker det automatisk - jeg får et unikt sæt af afdelinger.