Tabelle wird geändert, mutating table problem in oracle

dango

Neuer Benutzer
Beiträge
1
Ich habe einen Trigger implementiert, der die Löschung eines Kontos verhindert, wenn eine Zahlung erfolgt, aber ich erhalte den Fehler, dass die Tabelle geändert wurde. Im Internet steht, dass es das Problem der mutating table ist.

Ich habe es gerade mit dem Compound Trigger versucht, aber ich konnte es nicht zum Laufen bringen.

Hier zuerst der Code wie er vorher war
Code:
CREATE OR REPLACE TRIGGER checkDelete

BEFORE DELETE ON customerAcc

Declare

       price NUMBER;

       status NUMBER;

       delete_exception EXCEPTION; 

BEGIN

SELECT amount, paid INTO price, status FROM payment NATURAL JOIN tutoring

 WHERE customer_ID = :OLD.customer_ID;

IF price != status THEN

RAISE delete_exception;

END IF;

EXCEPTION

WHEN delete_exception THEN

DBMS_OUTPUT.PUT_LINE(Can not delete acc!');

END;


und hier der Code mit dem Compound Trigger ich habe versucht den Compound Trigger zu benutzen habe es aber nicht ganz verstanden und brauche Hilfe

Code:
CREATE OR REPLACE TRIGGER checkDelete

BEFORE DELETE ON customerAcc



Compound Trigger

TYPE r_check_delete is RECORD

                (

                price NUMBER;

                status NUMBER;

                delete_exception EXCEPTION;

                );





BEFORE EACH ROW IS

BEGIN

SELECT amount, paid INTO price, status FROM payment NATURAL JOIN tutoring

 WHERE customer_ID = :OLD.customer_ID;

IF price != status THEN

RAISE delete_exception;

END IF;

EXCEPTION

WHEN delete_exception THEN

DBMS_OUTPUT.PUT_LINE(Can not delete acc!');

END BEFORE EACH ROW;
END;

Es wäre cool wenn mir jemand mal anhand des Beispiels zeigen könnte wie es funktioniert
 
Werbung:
Für mich sieht das nicht nach einem mutating table Problem aus.
Kannst Du bitte prüfen, welche weiteren Trigger auf der Tabelle und auf den beteiligten Tabellen sowie auf Tabellen mit Cascading Delete Beteiligung liegen? (Bitte nicht das aufführen, was Du glaubst, sondern das was wirklich an den Tabellen hängt)
Warum Compound Trigger bewirken?
Und warum falsch und mit Type Record?
 
Wieso hast Du den Compound Trigger versucht? Was ist mit dem "alten" Trigger? Ist das einfach nur der erste Versuch?
Er ist jedenfalls besser als der Compound Trigger, also zur ersten Variante:

Number Deklaration der Variablen:
Hier würde ich etwas genauer arbeiten, da es offenbar um Kohle geht (und Du später auf Gleichheit prüfst)
Ein Konto, wo irgendwie blöd gerundet wird und am Ende nur 2 (zumindest in D) Nachkommastellen gebraucht werden, sollte auch einen passenden Typen verwenden. Money oder vergleichbare Typen gibt's bestimmt auch bei Oracle, weiß ich grad nicht.

Du kannst als Typ für die Variablen auch gleich genau den Typ der Spalten angeben, wo die Werte her kommen. Bei einer späteren Änderung der Tabellenspaltentypen dieser beiden Werte wird der Trigger dann kurz invalid und heilt sich idealerweise selbst mit dem neuen Typ, ansonsten manuell Recompile durchführen, aber nichts editieren.

Price, Status
Der Preis ist heiß! Wieso nimmst Du andere Namen für die Werte und warum so seltsame? Du willst doch Soll und Haben vergleichen oder?

delete_exception
Spendier noch den Tablename dazu, das hilft erheblich- auch den Leuten, die Logs lesen-, wenn man in einem großen System gleich die Quelle genannt bekommt.

Exception Handling
Du erzeugst selbst eine Exception mit Raise (ok), fängst sie aber dann gleich ab (auch ok erst mal) und gibst nur eine piepsige Meldung auf der Serverconsole aus. Du hast damit nichts verhindert am Löschvorgang, die Daten sind futsch und lesen wird die Meldung auch niemand im Betrieb- schon gar nicht der Nutzer.

Du musst die Exception nach WHEN .. THEN .. wieder Raisen, wenn Du sie extra produzierst, damit die Transaktion auch wirklich auf die Nase fliegt. Das Löschen soll nicht gehen, wenn das Konto nicht ausgeglichen ist. (nehme ich mal an)

Eigenlich noch wichtiger, immer, nicht nur in diesem Beispiel: Wenn Du selbst Exceptions abfängst, dann auch die restlichen Exceptions, die so auftreten können nicht vergessen. Du sagst in Deinem Code nur, "wenn dieser Fehler auftritt, gib einen Text aus". Mehr nicht, andere Fehler dürfen passieren, kein RAISE und werden nicht mal mehr ausgegeben. Das macht man mit einem weiteren Catch All Handler WHEN OTHERS THEN. (oder detaillierter, wenn man weiß, welche Fehler noch auftreten können.) Du willst ja nicht, dass die Löschung funktioniert, wenn ein unbedachter (idR sehr unangenehmer) Fehler auftritt.
Du kennst bestimmt die scheinbar dämliche Fehlermeldung "unerwarteter Fehler aufgetreten". Genau an der Stelle fehlt es in Deinem Trigger, die unerwarteten Fehler treten bei Dir auch auf, werden aber weder gemeldet noch wird die Transaktion unterbrochen.

Und noch mal zur Kern Exception, schau Dir an, wie man da auch Texte mitgibt, statt dieses DBMS_OUTPUT zu nutzen. Das kann man als Entwickler machen, aber für den Betrieb ist es nicht so dolle.
 
Werbung:
p.s.: für den gewollten Drama Effekt, kannst Du
EXCEPTION
WHEN ..
auch erstmal weglassen. Das Raise allein reicht und es knallt schön, genau wie bei jedem anderen Constraint oder sonstwas Fehler. Das ist besser als nur einen Fehler halb "abzufangen" und Du bewegst Dich damit funktional genau in diesem Bereich auf Ebene von Constraints.
Ich schreib es extra, weil es Systeme (vermutlich sogar Strategien) gibt, wo Fehler fast systematisch ignoriert werden, Hauptsache für den Benutzer "läuft es" nach außen hin.

Und mal positiv gesehen: Es ist immer sehr gut, die Datenkonsistenz mit sowas zu schützen. Besser, die ganze K.. steigt aus, als falsche Daten im System. So eine eigene Exception im Trigger oder Programmcode ist eine großartige Unterstützung von wichtigen DB Features wie Constraints usw.. Es ist so eine Art Bruteforce Schutz für das System. Das System ist erstmal save, selbst wenn ein ahnungsloser Admin oder so an den Tabellen rumdoktert.
Eine Ebene höher muss man nun die gleiche Prüfung machen wie im Trigger, nur bevor die Operation überhaupt angewendet wird / angeboten wird. Und einen netten Text dazu, dann freut sich sogar der Benutzer.
 
Zurück
Oben