Prova en sammansatt 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;
/
========REDIGERA - några frågor och svar ===========
F:Varför uppstår ett mutationstabellfel?
S:Detta beskrivs i dokumentationen:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
F:hur undviker man ett mutationstabellfel?
S:Dokumentationen rekommenderar användning av en coumpound-trigger, se detta:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
F:Vad är en sammansatt trigger och hur fungerar den?
S:Det här är ett stort ämne, vänligen se dokumentationen här:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
Kort sagt:detta är en speciell typ av trigger som gör det möjligt att kombinera fyra typer av separata triggers:BEFORE statement
, BEFORE-for each row
, AFTER for each row
och AFTER statament
till en en-deklaration. Det gör det lättare att implementera ett scenario där det finns ett behov av att överföra vissa data från en utlösare till en annan. Läs länken ovan för mer information.
F:Men vad gör egentligen "Departments( :new.department ) := :new.department;
?
S:Denna deklaration lagrar ett avdelningsnummer i en associativ array.
Denna array deklareras i en deklarativ del av den sammansatta triggern:
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
Dokumentationen relaterad till de sammansatta triggers säger att:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Ovanstående betyder att Departments
variabeln initieras endast en gång i början av hela bearbetningen, precis efter att triggern utlöses. "Firing-state duration" betyder att denna variabel förstörs efter att triggern är klar.
Detta uttalande:Departments( :new.department ) := :new.department;
lagrar ett avdelningsnummer i den associativa arrayen. Den finns i BEFORE EACH ROW
sektionen, exekveras den för varje rad som uppdateras (eller infogas) av update/insert-satsen.:new
och :old
är pseudorecords, mer om dem hittar du här: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
Kort sagt::new.department
hämtar ett nytt värde för department
kolumn- för en för närvarande uppdaterad rad (uppdaterat värde - EFTER uppdateringen), medan :old.department
ger ett gammalt värde för denna kolumn (INNAN uppdateringen).
Denna samling används senare i AFTER STATEMENT
, när triggarna väljer alla uppdaterade avdelningar (i en FOR-LOOP), för varje avdelning avfyras SELECT SUM(salary) ...
och kontrollerar sedan om denna summa är mindre än 1000
Tänk på en enkel uppdatering:UPDATE treballa SET salary = salary + 10
. Detta är ett enda uppdateringsuttryck, men ändrar många rader samtidigt. Ordningen för exekvering av vår trigger är följande:
- Uppdateringsstatistiken avfyras:
UPDATE treballa SET salary = salary + 10
- Den deklarativa delen av utlösaren exekveras, det vill säga:
Departments
variabeln initieras BEFORE EACH ROW
sektionen exekveras, separat för varje uppdaterad rad - så många gånger som det finns rader som ska uppdateras. På denna plats samlar vi alla avdelningar från ändrade rader.AFTER STATEMENT
avsnittet utförs. Vid det här laget är tabellen redan uppdaterad - alla rader har redan nya, uppdaterade löner. Vi går igenom avdelningar sparade iDepartments
och för var och en kontrollerar vi om summan av löner är mindre eller lika med 1000. Om denna summa är> 1000 för någon av dessa avdelningar, så kastas ett fel, och hela uppdateringen avbryts och rullas tillbaka. Annars avslutas triggern och uppdateringen är klar (men du måste genomföra dessa ändringar ändå).
F:Vad är en associativ array, och varför används just den här typen av samling, snarare än andra samlingar (en varray eller en kapslad tabell) ?
S:PL/SQL-samlingar är ett stort ämne. Följ den här länken för att lära dig dem:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
Kort sagt - Associativ array (eller index-by-tabell) är som en karta i java (hashmap, treemap etc) - det är en uppsättning nyckel-värdepar, och varje nyckel är unik . Du kan lägga samma nyckel många gånger i den här arrayen (med olika värden), men den här nyckeln kommer bara att lagras en gång - den är unik.
Jag har använt den för att få en unik uppsättning avdelningar.
Tänk på vårt uppdateringsexempel igen:UPDATE treballa SET salary = salary + 10
- detta kommando rör hundratals rader som har samma avdelning. Jag vill inte ha en samling med samma avdelning duplicerad 100 gånger, jag behöver en unik uppsättning avdelningar och jag vill köra vår fråga SELECT sum()...
endast en gång för varje avdelning, inte 100 gånger. Med hjälp av den sssociativa arrayen görs det automatiskt - jag får en unik uppsättning avdelningar.