Versuchen Sie es mit einem zusammengesetzten 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;
/
========BEARBEITEN - ein paar Fragen und Antworten ===========
F:Warum tritt ein Mutationstabellenfehler auf?
A:Dies ist in der Dokumentation beschrieben:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
F:Wie vermeide ich einen mutierenden Tabellenfehler?
A:Die Dokumentation empfiehlt die Verwendung eines Compound-Triggers, siehe hier:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
F:Was ist ein zusammengesetzter Trigger und wie funktioniert er?
A:Dies ist ein großes Thema, bitte lesen Sie die Dokumentation hier:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
Kurz gesagt:Dies ist eine spezielle Art von Trigger, die es ermöglicht, vier Arten von separaten Triggern zu kombinieren:BEFORE statement
, BEFORE-for each row
, AFTER for each row
und AFTER statament
in eine eine Deklaration. Es erleichtert die Implementierung einiger Szenarien, in denen einige Daten von einem Trigger an einen anderen übergeben werden müssen. Bitte studieren Sie den obigen Link für weitere Details.
F:Aber was bedeutet eigentlich "Departments( :new.department ) := :new.department;
?
A:Diese Deklaration speichert eine Abteilungsnummer in einem assoziativen Array.
Dieses Array wird in einem deklarativen Teil des zusammengesetzten Triggers deklariert:
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
Die Dokumentation zu den zusammengesetzten Triggern besagt Folgendes:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Das Obige bedeutet, dass Departments
Die Variable wird nur einmal zu Beginn der gesamten Verarbeitung initialisiert, unmittelbar nachdem der Trigger ausgelöst wurde. "Auslöseanweisungsdauer" bedeutet, dass diese Variable zerstört wird, nachdem der Trigger beendet ist.
Diese Anweisung:Departments( :new.department ) := :new.department;
speichert eine Abteilungsnummer im assoziativen Array. Es steht in BEFORE EACH ROW
Abschnitt, dann wird es für jede Zeile ausgeführt, die durch die update/insert-Anweisung aktualisiert (oder eingefügt) wird.:new
und :old
sind Pseudorecords, mehr dazu finden Sie hier: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
Kurz::new.department
ruft einen neuen Wert von department
ab Spalte- für eine aktuell aktualisierte Zeile (aktualisierter Wert - NACH der Aktualisierung), während :old.department
gibt einen alten Wert dieser Spalte an (VOR dem Update).
Diese Sammlung wird später im AFTER STATEMENT
verwendet , wenn die Trigger alle aktualisierten Abteilungen (in einer FOR-LOOP) auswählen, wird für jede Abteilung SELECT SUM(salary) ...
ausgelöst und prüft dann, ob diese Summe kleiner als 1000 ist
Erwägen Sie ein einfaches Update:UPDATE treballa SET salary = salary + 10
. Dies ist eine einzelne Update-Anweisung, ändert jedoch viele Zeilen auf einmal. Die Reihenfolge der Ausführung unseres Triggers ist wie folgt:
- Das Update-Statement wird ausgelöst:
UPDATE treballa SET salary = salary + 10
- Der deklarative Abschnitt des Triggers wird ausgeführt, das heißt:
Departments
Variable wird initialisiert BEFORE EACH ROW
Abschnitt ausgeführt wird, separat für jede aktualisierte Zeile - so oft, wie Zeilen zu aktualisieren sind. An dieser Stelle sammeln wir alle Abteilungen aus geänderten Reihen.AFTER STATEMENT
Abschnitt ausgeführt wird. An dieser Stelle ist die Tabelle bereits aktualisiert – alle Zeilen haben bereits neue, aktualisierte Gehälter. Wir durchlaufen die Abteilungen, die inDepartments
gespeichert sind und für jede überprüfen wir, ob die Summe der Gehälter kleiner oder gleich 1000 ist. Wenn diese Summe für eine dieser Abteilungen> 1000 ist, wird ein Fehler ausgegeben, und die gesamte Aktualisierung wird abgebrochen und rückgängig gemacht. Andernfalls wird der Trigger beendet und die Aktualisierung durchgeführt (aber Sie müssen diese Änderungen trotzdem festschreiben).
F:Was ist ein assoziatives Array und warum wird gerade diese Art von Sammlung verwendet und nicht andere Sammlungen (ein varray oder eine verschachtelte Tabelle)?
A:PL/SQL-Sammlungen sind ein großes Thema. Folgen Sie diesem Link, um sie zu lernen:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
Kurz gesagt - Assoziatives Array (oder Index-by-Tabelle) ist wie eine Karte in Java (Hashmap, Treemap usw.) - es ist ein Satz von Schlüssel-Wert-Paaren, und jeder Schlüssel ist einzigartig . Sie können denselben Schlüssel viele Male in dieses Array einfügen (mit unterschiedlichen Werten), aber dieser Schlüssel wird nur einmal gespeichert - er ist eindeutig.
Ich habe ihn verwendet, um einen eindeutigen Satz von Abteilungen zu erhalten.
Betrachten Sie noch einmal unser Update-Beispiel:UPDATE treballa SET salary = salary + 10
- Dieser Befehl berührt Hunderte von Zeilen, die dieselbe Abteilung haben. Ich möchte nicht, dass eine Sammlung mit derselben Abteilung 100 Mal dupliziert wird, ich brauche einen eindeutigen Satz von Abteilungen, und ich möchte unsere Abfrage SELECT sum()...
ausführen nur einmal für jede Abteilung, nicht 100 Mal. Mit Hilfe des sssociative Arrays wird es automatisch gemacht - ich bekomme eindeutige Abteilungen.