ORA-04091: tableau [bla] est en mutation, de déclenchement et de fonction ne peuvent pas le voir
J'ai récemment commencé à travailler sur des applications complexes, et j'ai été affecté d'un bug à cause de cette erreur:
ORA-04091: table SCMA.TBL1 is mutating, trigger/function may not see it
ORA-06512: at "SCMA.TRG_T1_TBL1_COL1", line 4
ORA-04088: error during execution of trigger 'SCMA.TRG_T1_TBL1_COL1'
Le déclencheur en question ressemble à
create or replace TRIGGER TRG_T1_TBL1_COL1
BEFORE INSERT OR UPDATE OF t1_appnt_evnt_id ON TBL1
FOR EACH ROW
WHEN (NEW.t1_prnt_t1_pk is not null)
DECLARE
v_reassign_count number(20);
BEGIN
select count(t1_pk) INTO v_reassign_count from TBL1
where t1_appnt_evnt_id=:new.t1_appnt_evnt_id and t1_prnt_t1_pk is not null;
IF (v_reassign_count > 0) THEN
RAISE_APPLICATION_ERROR(-20013, 'Multiple reassignments not allowed');
END IF;
END;
La table possède une clé primaire "t1_pk
", "rendez-vous de l'id d'événement"
t1_appnt_evnt_id
et une autre colonne "t1_prnt_t1_pk
" qui peut ou
contient pas d'autre ligne t1_pk
.
Il semble que le déclencheur est essayer de faire en sorte que personne d'autre avec le
même t1_appnt_evnt_id
a visée à la même cette ligne fait référence à un renvoi vers une autre ligne, si celui-ci est en se référant à une autre ligne.
Le commentaire sur le rapport de bug à partir de l'administrateur de la base, dit "de retirer le déclencheur, et effectuer la vérification dans le code", mais malheureusement, ils ont un code de propriété génération couches sur le dessus de Hibernate, donc je ne peux même pas savoir où il est écrit, donc je suis en espérant qu'il y est un moyen de faire déclencher le travail. Qui est là?
- L'application de règles comme ceci seulement dans le code est une mauvaise idée - plusieurs mises à jour simultanées sont difficiles à gérer. Si vous synchronisez votre code, vous pouvez vous retrouver avec sale de blocages entre celle-ci et la base de données de serrures.
- Bas de ligne d'Oracle déclenche le sucer. Éviter comme la peste, sauf pour des choses aussi simple que la mise à jour des valeurs de séquence, ou "updated_by" champs de type. Leurs déclencheurs aspiré dans les années '90 et ils sucer maintenant.
Vous devez vous connecter pour publier un commentaire.
Je pense que je suis en désaccord avec votre description de ce que le déclencheur est d'essayer de
n'. Il me semble qu'il est censé faire respecter cette règle d'entreprise: Pour un
compte tenu de la valeur de t1_appnt_event, une seule ligne peut avoir une valeur non NULLE de
t1_prnt_t1_pk à la fois. (Il n'a pas d'importance si elles ont la même valeur dans la deuxième colonne ou pas.)
Fait intéressant, il est défini pour la mise à JOUR DE t1_appnt_event mais pas pour l'autre colonne, donc je pense que quelqu'un pourrait briser la règle par la mise à jour de la deuxième colonne, sauf si il y a un autre déclencheur de cette colonne.
Il y a peut être une façon pour vous de créer une fonction d'index de base qui applique cette règle de sorte que vous pouvez vous débarrasser de la gâchette entièrement. Je suis venu avec une façon, mais il nécessite quelques hypothèses:
Si ces hypothèses sont vraies, vous pouvez créer une fonction comme ceci:
et un index comme ceci:
Donc les lignes où le PMNT colonne est NULL apparaissent dans l'index avec l'inverse de la clé primaire que la deuxième valeur, de sorte qu'ils ne seraient jamais en conflit les uns avec les autres. Les lignes où il n'est pas NULL utiliser le réel (positif) de la valeur de la colonne. La seule façon vous pouvez recevoir une violation de contrainte serait si deux lignes avaient les mêmes valeurs non NULLES dans les deux colonnes.
C'est peut-être trop "sage", mais il peut vous aider à obtenir autour de votre problème.
Mise à jour de Paul Tomblin: je suis allé avec la mise à jour à l'origine, l'idée que igor mettre dans les commentaires:
Je suis d'accord avec Dave que le résultat souhaité probalby peut et doit être réalisée à l'aide intégrée dans les contraintes telles que les index uniques (ou contraintes uniques).
Si vous avez vraiment besoin pour obtenir autour de la mutation erreur de table, la manière habituelle de le faire est de créer un paquet qui contient un paquet d'étendue variable est un tableau de quelque chose qui peut être utilisé pour identifier les lignes modifiées (je pense que ROWID est possible, sinon, vous devrez utiliser le PK, je n'ai pas utiliser Oracle actuellement donc je ne peux pas tester). POUR CHAQUE LIGNE de déclenchement remplit ensuite dans cette variable, avec toutes les lignes qui sont modifiées par l'instruction, et puis il y a un APRÈS chaque déclaration de déclenchement qui lit les lignes et les valider.
Quelque chose comme (la syntaxe est probablement erronée, je n'ai pas travaillé avec Oracle pour quelques années)
Avec de la détente (ou le code de l'application) et de la solution dont vous avez besoin pour
mettre en de verrouillage pour éviter la corruption des données dans un environnement multi-utilisateur.
Même si votre déclencheur a travaillé ou a été ré-écrit pour éviter la mutation du tableau
problème, il n'empêcherait pas les 2 utilisateurs simultanément la mise à jour
t1_appnt_evnt_id la même valeur sur les lignes où t1_appnt_evnt_id n'est pas
null: supposons qu'il y ait currenly pas de lignes où t1_appnt_evnt_id=123 et
t1_prnt_t1_pk n'est pas nulle:
Vous avez maintenant une base de données corrompue!
La façon de l'éviter (dans le déclenchement ou le code de l'application) serait de verrouillage
le parent de lignes dans la table référencée par t1_appnt_evnt_id=123 avant d'effectuer la vérification:
Maintenant la session 2 du déclencheur doit attendre pour la session de 1 à commit ou rollback avant qu'il effectue le contrôle.
Il serait beaucoup plus simple et plus sûr de mettre en œuvre Dave Costa index!
Enfin, je suis content que personne n'a suggéré d'ajouter PRAGMA AUTONOMOUS_TRANSACTION pour votre détente: il est souvent proposé sur les forums et travaille en tant que la mutation du tableau de la question va au-loin - mais il rend l'intégrité des données problème encore pire! Donc il suffit de ne pas...
J'ai eu la même erreur avec la veille prolongée. Et le rinçage de la session en utilisant
résolu ce problème pour moi. (Je ne suis pas poster mon bloc de code que j'étais sûr que tout a été correctement écrit et travail - mais il n'était pas jusqu'à ce que j'ai ajouté de la précédente flush() déclaration). Peut-être que cela peut aider quelqu'un.