Prova un trigger composto:
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 - alcune domande e risposte ===========
D:Perché si verifica un errore di tabella mutante?
R:Questo è descritto nella documentazione:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
D:come evitare un errore di tabella mutante?
R:La documentazione consiglia l'uso di un trigger cumulativo, vedere questo:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
D:Che cos'è un trigger composto e come funziona?
R:Questo è un argomento molto vasto, fare riferimento alla documentazione qui:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
In breve:questo è un tipo speciale di trigger che rende possibile la combinazione di quattro tipi di trigger separati:BEFORE statement
, BEFORE-for each row
, AFTER for each row
e AFTER statament
in una dichiarazione. Semplifica l'implementazione di alcuni scenari in cui è necessario passare alcuni dati da un trigger a un altro. Si prega di studiare il link sopra per maggiori dettagli.
D:Ma cosa significa effettivamente "Departments( :new.department ) := :new.department;
?
R:Questa dichiarazione memorizza un numero di reparto in un array associativo.
Questo array è dichiarato in una parte dichiarativa del trigger composto:
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
La documentazione relativa ai trigger composti dice che:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Quanto sopra significa che Departments
La variabile viene inizializzata solo una volta all'inizio dell'intera elaborazione, subito dopo l'attivazione del trigger. "Durata dell'istruzione di licenziamento" significa che questa variabile viene distrutta al termine del trigger.
Questa istruzione:Departments( :new.department ) := :new.department;
memorizza un numero di reparto nella matrice associativa. È in BEFORE EACH ROW
sezione, quindi viene eseguito per ogni riga che viene aggiornata (o inserita) dall'istruzione update/insert.:new
e :old
sono pseudorecord, puoi trovarne di più qui: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
In breve::new.department
recupera un nuovo valore di department
column- per una riga attualmente aggiornata (valore aggiornato - DOPO l'aggiornamento), mentre :old.department
fornisce un vecchio valore di questa colonna (PRIMA dell'aggiornamento).
Questa raccolta viene successivamente utilizzata in AFTER STATEMENT
, quando i trigger selezionano tutti i reparti aggiornati (in un FOR-LOOP), per ogni reparto viene attivato SELECT SUM(salary) ...
e poi controlla se questa somma è inferiore a 1000
Considera un semplice aggiornamento:UPDATE treballa SET salary = salary + 10
. Questa è una singola istruzione di aggiornamento, ma cambia molte righe contemporaneamente. L'ordine di esecuzione del nostro trigger è il seguente:
- Viene licenziato lo stato di aggiornamento:
UPDATE treballa SET salary = salary + 10
- Viene eseguita la sezione dichiarativa del trigger, ovvero:
Departments
viene inizializzata la variabile BEFORE EACH ROW
la sezione viene eseguita, separatamente per ogni riga aggiornata, tante volte quante sono le righe da aggiornare. In questo luogo raccogliamo tutti i reparti dalle righe modificate.AFTER STATEMENT
la sezione viene eseguita. A questo punto la tabella è già aggiornata:tutte le righe hanno già stipendi nuovi e aggiornati. Esaminiamo i reparti salvati inDepartments
e per ciascuno controlliamo se la somma degli stipendi è minore o uguale a 1000. Se questa somma è> 1000 per uno qualsiasi di questi dipartimenti, viene generato un errore e l'intero aggiornamento viene interrotto e ripristinato. In caso contrario, il trigger termina e l'aggiornamento è terminato (ma è comunque necessario eseguire il commit di queste modifiche).
D:Che cos'è un array associativo e perché viene utilizzato solo questo tipo di raccolta, anziché altre raccolte (una variabile o una tabella nidificata)?
R:Le raccolte PL/SQL sono un argomento molto vasto. Segui questo link per impararli:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
In breve - L'array associativo (o indice per tabella) è come una mappa in java (hashmap, treemap ecc.) - è un insieme di coppie chiave-valore e ogni chiave è unico . Puoi inserire la stessa chiave più volte in questo array (con valori diversi), ma questa chiave verrà archiviata solo una volta:è univoca.
L'ho usata per ottenere un insieme univoco di reparti.
Considera ancora il nostro esempio di aggiornamento:UPDATE treballa SET salary = salary + 10
- questo comando tocca centinaia di righe che hanno lo stesso reparto. Non voglio che una raccolta con lo stesso reparto venga duplicata 100 volte, ho bisogno di un insieme univoco di reparti e voglio eseguire la nostra query SELECT sum()...
solo una volta per reparto, non 100 volte. Con l'aiuto dell'array associativo viene eseguito automaticamente:ottengo un set unico di dipartimenti.