[ORACLE - TRIGGER]exception TABLE IS MUTATING

exception TABLE IS MUTATING [ORACLE - TRIGGER] - SQL/NoSQL - Programmation

Marsh Posté le 16-06-2008 à 18:15:19    

Bonjour,
 
j'ai besoin d'aide pour mon trigger sous Oracle 10g.
 
Je recupére des données (information d'employé) d'un fichier csv grâce à SQL*Loader que je dois mettre dans une table t_cedd_main. Je souhaiterais avec mon trigger inserer ou mettre a jour les lignes de ma tables suivant s'il y a de nouveaux employés dans le fichier csv ou bien des données modifiées.
 
Voici ce que j'ai fait pour l'instant:
 

Code :
  1. create or replace trigger trg_CxDD_CEDD
  2.   before insert on t_cedd_main 
  3.   for each row
  4. declare
  5.   -- local variables here
  6.   rowcnt number;
  7.   DO_UPDATE boolean;
  8.  
  9. begin
  10.   rowcnt := 0;
  11.   DO_UPDATE := false;
  12.  
  13.     SELECT COUNT(*) INTO rowcnt FROM t_CEDD_MAIN m WHERE m.PersonIdNo = :new.PersonIdNo;
  14.    
  15.   -- New employee?
  16.   IF (rowcnt = 0) THEN
  17.        -- Insert the new employee.
  18.        INSERT INTO t_CEDD_MAIN
  19.        (
  20.           PersonIdNo, FirstName, MiddleName, LastName, ....
  21.        )
  22.        VALUES
  23.        (
  24.           :new.PersonIdNo, :new.FirstName, :new.MiddleName,  ... 
  25.        );
  26.      
  27.   ELSIF (rowcnt = 1) THEN
  28.      
  29.        -- FirstName
  30.        IF (:old.FirstName is null) THEN
  31.           IF (:new.FirstName is not null) THEN
  32.              DO_UPDATE := true;
  33.           END IF;
  34.        ELSE
  35.           IF ((trim(:new.FirstName) <> trim(:old.FirstName)) OR (:new.FirstName is null)) THEN
  36.              DO_UPDATE := true;
  37.           END IF;
  38.        END IF;
  39.      
  40.        -- MiddleName
  41.        IF (DO_UPDATE = false) THEN
  42.          IF (:old.MiddleName is null) THEN
  43.             IF (:new.MiddleName is not null) THEN
  44.                DO_UPDATE := true;
  45.             END IF;
  46.          ELSE
  47.             IF ((trim(:new.MiddleName) <> trim(:old.MiddleName)) OR (:new.MiddleName is null)) THEN
  48.                DO_UPDATE := true;
  49.             END IF;
  50.          END IF;
  51.        END IF; 
  52.      
  53.        ....
  54.      
  55.        IF (DO_UPDATE = TRUE) THEN
  56.           -- at least 1 field value in the newly imported data is different from the field
  57.           -- value in t_CEDD_Main for this employee. Update 'Main' with the new data.
  58.          
  59.           UPDATE t_CEDD_MAIN m
  60.           SET
  61.             m.PersonIdNo = :new.PersonIdNo, m.FirstName = :new.FirstName, m.MiddleName = :new.MiddleName, ...
  62.         WHERE m.PersonIdNo = :new.PersonIdNo;
  63.            
  64.  END IF;
  65.   END IF;
  66. END trg_CxDD_CEDD;


 
Le probleme de ce trigger c'Est ce que j'ai une exception ORA-04091 TABLE IS MUTATING. D'apres ce que jai pu trouvé sur google, le probleme viendrait que j'ai un trigger de niveau ligne sur ma table t_cedd_main et qu'a l'interieur de ce trigger j'ai un SELECT. Or vu que je voudrait juste ajouter les nouveaux employés et mettre a jour les nouveaux, je ne vois pas du tout comment faire autrement. Je ne peux pas me permettre de vider toute la table et remettre tout a chaque fois car ce trigger devra etre executer tous les jours.
 
Toutes aides seraient la bienvenue.
 
A+

Message cité 1 fois
Message édité par cervantes le 16-06-2008 à 18:16:01
Reply

Marsh Posté le 16-06-2008 à 18:15:19   

Reply

Marsh Posté le 16-06-2008 à 18:42:09    

oui tout a fait. C'est cette ligne la qui merde  
"SELECT COUNT(*) INTO rowcnt FROM t_CEDD_MAIN m WHERE m.PersonIdNo = :new.PersonIdNo;"
 
Ne me dit pas qu'il n'y a pas moyen, c'est des conneries ça :)
ton trigger est ultra mal foutu.  
 
donc ce que tu fais la, c'est avant chaque insert ou update dans ta table, tu redéclenche un insert ou un update automatiquement? Tu sais que c'est complètement abérant?
 
Ce genre de trigger, c'est pour modifier la ligne qui est en cours d'insertion ou de modification, pour pour générer encore une panoplie de data dans la même table.

Reply

Marsh Posté le 16-06-2008 à 18:58:44    

Il faut bien pouvoir faire le test si ma ligne est un nouvel employé ou non. Si je le fais pas dans mon trigger ou le faire. Je doute pas un instant que mon trigger est nul, c'est le premier que j'ecris.

Reply

Marsh Posté le 16-06-2008 à 20:23:38    

la preuve que tu n'as rien compris aux trigger ;-)
 
Je me permets de te renvoyer vers la doc concernant les triggers. Bonne lecture.

Reply

Marsh Posté le 17-06-2008 à 13:06:38    

Je suis tombé sur une fonction assez interessante: MERGE. Je pense qu'il serait en fait plus rapide que je remplisse une table temporaire avec SQL*LOADER sans avoir a declencher de trigger et apres un job je fais un Merge. Jsuis egalement tombé sur une autre fonction qui m'a l'air assez interessante: MINUS pour supprimer les employes virés.

Reply

Marsh Posté le 17-06-2008 à 19:37:42    

ce n'est pas impossible avec un trigger. je pense que tu as simplement pas bien compris son fonctionnement.

Reply

Marsh Posté le 17-06-2008 à 20:36:12    

+1 avec moi23372, ceci n'a rien à faire dans un trigger.


Message édité par Harkonnen le 17-06-2008 à 20:36:22

---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 17-06-2008 à 20:43:51    

cervantes a écrit :

Il faut bien pouvoir faire le test si ma ligne est un nouvel employé ou non. Si je le fais pas dans mon trigger ou le faire. Je doute pas un instant que mon trigger est nul, c'est le premier que j'ecris.


dans le code client ou à la rigueur dans une procédure stockée. un trigger permet de réaliser des opérations sur une table en fonction d'évenements arrivant sur d'autres tables, il ne sert pas de remplaçant à if....else ou à switch....case


Message édité par Harkonnen le 17-06-2008 à 20:44:07

---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 28-08-2008 à 16:56:03    

[quotemsg=1747140,1,8513]Bonjour,
 
j'ai besoin d'aide pour mon trigger sous Oracle 10g.
 
Je recupére des données (information d'employé) d'un fichier csv grâce à SQL*Loader que je dois mettre dans une table t_cedd_main. Je souhaiterais avec mon trigger inserer ou mettre a jour les lignes de ma tables suivant s'il y a de nouveaux employés dans le fichier csv ou bien des données modifiée
 
##############################################
 
Il existe deux types de triggers.
 
STATEMENT (DÉFAUT)
EXÉCUTÉ 1 FOIS
NE PERMET PAS DE TRAVAILLER DANS LE TRIGGER POUR CHAQUE TUPLE SUR LES ANCIENNES VALEURS ET LES NOUVELLES VALEURS
 
ROW (FOR EACH ROW)      
EXÉCUTÉ AUTANT DE FOIS QUE DE TUPLES TOUCHÉS PAR LA MAJ
PERMET DE TRAVAILLER DANS LE TRIGGER SUR CHAQUE TUPLE MIS À JOUR
(ANCIENNES ET NOUVELLES VALEURS)
CONTRAINTES DANS LE PL / SQL
MAIS NE PERMET PAS DE MANIPULER LA TABLE SU LAQUELLE EST POSER LE TRIGGER, ON PEUT UNIQUEMENT MODIFIER LES DONNEES new. En cas de BEFORE
 
 
EXEMLE:
 
 
- solution avec un « STATEMENT TRIGGER »
 
On laisse s’effectuer d’abord toutes les mises à jour et on vérifie après coup que celles-ci n’engendrent pas la présence de plus d’un président.
*/
create or replace trigger check_president1
after update of job or insert on emp
declare
nbpresident number(2,0);
begin
select count (*) into nbpresident from emp where job = PRESIDENT';
if nbpresident > 1 then  
raise_application_error ( -20002, 'IL NE PEUT Y AVOIR PLUS D UN PRESIDENT');
end if;
end;
/
 
- solution avec un « FOR EACH ROW TRIGGER »
 
On ne peut dans le PL /SQL d’un FOR EACH ROW TRIGGER faire de SELECT sur la table sur laquelle ce trigger est défini. La solution est donc plus complexe et doit faire intervenir une copie (partielle),table miroir de la table initiale sur laquelle la mise à jour est propagée.
*/
drop table empbis ;
create table empbis as select empno, job, mgr from emp ;
create or replace trigger propage
after delete or update or insert on emp
for each row
begin
  if inserting then
         insert into empbis values (:new.empno, :new.job,:new.mgr);
  elsif deleting then
         delete from empbis where empno = :old.empno;
  elsif updating then  
         update empbis set job= :new.job,mgr = :new.mgr where empno =:old.empno;
  end if;
end ;
/
/*
 
 Si tout se passe bien dans les mises à jour de la table emp , ces mises à jour seront automatiquement transférées dans empbis.
 Il suffit maintenant de poser un trigger en before sur emp qui reportera le select sur la table empbis.
*/
 
 
 
drop trigger check_president1 ;
 
create or replace trigger check_president2
before update of job or insert on emp
for each row
when (new.job = 'PRESIDENT')
declare
nbpresident number(2,0);
begin
select count (*) into nbpresident from empbis where job = 'PRESIDENT';
if nbpresident > 0 then  
raise_application_error ( -20002, 'IL NE PEUT Y AVOIR PLUS D UN PRESIDENT');
end if;
end;
/*
 
On peut bien sûr envisager d’ autres combinaisons d’évènements et de timing de « FOR EACH ROW TRIGGER ».  
Noter que le test sur nbpresident a changé !
 
j'espere que ca va t'aidé
 


Message édité par severnaja le 28-08-2008 à 16:58:05
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed