Essayez un déclencheur composé :
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 - quelques questions et réponses ===========
Q :Pourquoi une erreur de table en mutation se produit-elle ?
R :Ceci est décrit dans la documentation :
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
Q :comment éviter une erreur de table en mutation ?
R :La documentation recommande l'utilisation d'un déclencheur composé, voir ceci :http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
Q :Qu'est-ce qu'un déclencheur composé et comment fonctionne-t-il ?
R :C'est un vaste sujet, veuillez consulter la documentation ici :http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
En bref :il s'agit d'un type spécial de déclencheur qui permet de combiner quatre types de déclencheurs distincts :BEFORE statement
, BEFORE-for each row
, AFTER for each row
et AFTER statament
en une seule déclaration. Cela facilite la mise en œuvre de certains scénarios dans lesquels il est nécessaire de transmettre certaines données d'un déclencheur à un autre. Veuillez étudier le lien ci-dessus pour plus de détails.
Q :Mais que signifie réellement "Departments( :new.department ) := :new.department;
?
A :Cette déclaration stocke un numéro de service dans un tableau associatif.
Ce tableau est déclaré dans une partie déclarative du déclencheur composé :
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
La documentation relative aux déclencheurs composés indique que :http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Ce qui précède signifie que Departments
La variable est initialisée une seule fois au début de l'ensemble du traitement, juste après le déclenchement du déclencheur. "Firing-statement duration" signifie que cette variable est détruite après la fin du déclencheur.
Cette instruction :Departments( :new.department ) := :new.department;
stocke un numéro de département dans le tableau associatif. C'est dans BEFORE EACH ROW
section, puis elle est exécutée pour chaque ligne mise à jour (ou insérée) par l'instruction update/insert.:new
et :old
sont des pseudo-enregistrements, vous pouvez en trouver plus ici : http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
En bref ::new.department
récupère une nouvelle valeur de department
colonne - pour une ligne actuellement mise à jour (valeur mise à jour - APRÈS la mise à jour), tandis que :old.department
donne une ancienne valeur de cette colonne (AVANT la mise à jour).
Cette collection est ensuite utilisée dans le AFTER STATEMENT
, lorsque les déclencheurs sélectionnent tous les départements mis à jour (dans une boucle FOR), pour chaque département déclenche SELECT SUM(salary) ...
et vérifie ensuite si cette somme est inférieure à 1000
Envisagez une simple mise à jour :UPDATE treballa SET salary = salary + 10
. Il s'agit d'une instruction de mise à jour unique, mais modifie plusieurs lignes à la fois. L'ordre d'exécution de notre trigger est le suivant :
- L'état de mise à jour est déclenché :
UPDATE treballa SET salary = salary + 10
- La section déclarative du déclencheur est exécutée, c'est-à-dire :
Departments
la variable est initialisée BEFORE EACH ROW
section est exécutée, séparément pour chaque ligne mise à jour - autant de fois qu'il y a de lignes à mettre à jour. À cet endroit, nous collectons tous les départements des lignes modifiées.AFTER STATEMENT
section est exécutée. À ce stade, le tableau est déjà mis à jour - toutes les lignes ont déjà de nouveaux salaires mis à jour. Nous parcourons les départements enregistrés dansDepartments
et pour chacun, nous vérifions si la somme des salaires est inférieure ou égale à 1000. Si cette somme est> 1000 pour l'un de ces départements, une erreur est renvoyée et toute la mise à jour est annulée et annulée. Sinon, le déclencheur se termine et la mise à jour est terminée (mais vous devez quand même valider ces modifications).
Q :Qu'est-ce qu'un tableau associatif et pourquoi ce type de collection est-il utilisé plutôt que d'autres collections (un varray ou une table imbriquée) ?
R :Les collections PL/SQL sont un vaste sujet. Suivez ce lien pour les apprendre :http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
En bref - Un tableau associatif (ou une table indexée) est comme une carte en Java (hashmap, treemap, etc.) - c'est un ensemble de paires clé-valeur, et chaque clé est uniques . Vous pouvez mettre la même clé plusieurs fois dans ce tableau (avec des valeurs différentes), mais cette clé ne sera stockée qu'une seule fois - elle est unique.
Je l'ai utilisée pour obtenir un ensemble unique de départements.
Reprenons notre exemple de mise à jour :UPDATE treballa SET salary = salary + 10
- cette commande touche des centaines de lignes qui ont le même département. Je ne veux pas qu'une collection avec le même département soit dupliquée 100 fois, j'ai besoin d'un ensemble unique de départements, et je veux exécuter notre requête SELECT sum()...
une seule fois pour chaque département, pas 100 fois. Avec l'aide du tableau sssociatif, cela se fait automatiquement - j'obtiens un ensemble unique de départements.