Oracle
 sql >> Database >  >> RDS >> Oracle

Aggiorna Trigger PL/SQL Oracle

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:

  1. Viene licenziato lo stato di aggiornamento:UPDATE treballa SET salary = salary + 10
  2. Viene eseguita la sezione dichiarativa del trigger, ovvero:Departments viene inizializzata la variabile
  3. 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.
  4. 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 in Departments 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.