4 votes

En utilisant des scripts pour assurer l'intégrité des données dans Oracle

Est-il mauvais de recourir à des déclencheurs ou des scripts pour maintenir l'intégrité des données qu'Oracle n'est pas conçu pour imposer, ou est-ce un signe que je modélise mal mes données?

D'après les réponses à un précédent post (Mise en œuvre des champs définis par l'utilisateur), j'ai décidé de continuer à concevoir en utilisant un mélange d'Héritage de classe et d'Héritage concret. Je veux une classe de base pour tous les SAMPLE puis une table concrète pour chaque ensemble unique d'attributs.

Alors que je peux imposer que chaque table concrète ait une entrée parent dans SAMPLE en faisant de SAMPLE.sample_id la clé primaire avec une contrainte de clé étrangère. Cependant, je ne sais pas comment imposer qu'une entrée SAMPLE ait exactement un enfant puisque l'entrée enfant pourrait être dans un nombre quelconque de tables.

Comment puis-je imposer cela? Si la solution est des déclencheurs INSERT, UPDATE et DELETE, est-ce considéré comme une mauvaise pratique?

0voto

Christopher Points 5252

Eh bien, tout dépend du type d'intégrité que vous recherchez.

La base de données est conçue pour l'intégrité référentielle. Donc, si c'est ce que vous recherchez, utilisez les structures de la base de données. Ne créez pas le vôtre.

Si vous essayez de maintenir d'autres types d'intégrité (comme les MAC pour les lignes), alors les déclencheurs sont tout à fait acceptables.

0voto

Steve Broberg Points 2012

Après avoir commenté dans quelques-unes des réponses ici, je me sens obligé de montrer un exemple de comment les déclencheurs peuvent être utilisés pour faire respecter des règles qui vont au-delà de la RI de base fournie par les SGBDR.

Dans notre propre système, nous avons plusieurs tables qui ont toutes des clés étrangères pointant vers la même table ("MasterTable"). Au fil du temps, les lignes de ces tables seront supprimées ; une fois que la dernière ligne enfant a été supprimée, nous voulons supprimer la ligne parent et prendre des mesures. Nous avons imposé cette règle en créant une colonne "ChildCount" sur la table parent, qui indique le nombre de lignes qui se réfèrent à celle-ci (en d'autres termes, un décompte de références).

Dans les déclencheurs d'insertion de toutes les différentes tables enfant, nous avons du code qui ressemble à ceci :

      SELECT ChildCount
        INTO numrows
        FROM MasterTable mt
       WHERE :new.MasterTableId = MasterTable.MasterTableId
         FOR UPDATE;

      UPDATE MasterTable
         SET ChildCount = ChildCount + 1
       WHERE :new.MasterTableId = MasterTable.MasterTableId;

Dans les déclencheurs de suppression des tables enfant, nous avons ceci :

      SELECT ChildCount
        INTO numrows
        FROM MasterTable
       WHERE :old.MasterTableId = MasterTable.MasterTableId
         FOR UPDATE;

      DELETE MasterTable
       WHERE ChildCount = 1
         AND :old.MasterTableId = MasterTable.MasterTableId;

    IF Sql%RowCount = 0 THEN
         UPDATE MasterTable
            SET ChildCount = ChildCount - 1
          WHERE :old.MasterTableId = MasterTable.MasterTableId;
    END IF;

Les déclencheurs de mise à jour contiennent les deux morceaux de code.

La partie clé de cette logique est l'utilisation d'une déclaration select séparée avec la clause FOR UPDATE, au lieu de simplement mettre à jour la colonne avec une seule déclaration. Cela garantit que les transactions simultanées seront sérialisées correctement.

Étant donné que MasterTable a déjà déclaré des règles de suppression en cascade vers toutes les tables enfant, le code ci-dessus provoquera des erreurs ORA-04091 (table en mutation) lorsqu'il s'exécutera dans le contexte de la suppression d'une ligne MasterTable qui a des enfants existants, donc ces déclarations sont faites dans le contexte d'un bloc d'EXCEPTION qui intercepte et ignore cette erreur.

Enfin, le code ci-dessus est généré automatiquement pour nous à partir de l'outil CASE que nous utilisons pour la modélisation des données (ERWin). ERWin vous permet de créer des "propriétés définies par l'utilisateur" (UDP), et il dispose d'un langage de macro qui peut être utilisé pour générer virtuellement n'importe quel code dont vous avez besoin en fonction de votre schéma, donc tout ce que nous avons à faire pour activer cette fonctionnalité est d'ajouter la colonne ChildCount à la table parent appropriée et de définir l'UDP pour "Relation avec comptage de références" sur true.

Comme je l'ai souligné dans les commentaires ci-dessus, les déclencheurs ne peuvent pas être utilisés pour remplacer complètement la RI déclarée, puisque vous ne pouvez pas utiliser FOR UPDATE pour faire fonctionner correctement les règles de suppression en cascade. Mais c'est génial pour des règles supplémentaires comme celles-ci.

Remarque : Ce code est en production depuis maintenant 11 ans - il a été conçu à l'époque où nous utilisions encore Oracle 7. Si quelqu'un a une façon plus moderne de faire cela en utilisant les fonctionnalités intégrées d'Oracle, je serais intéressé de l'entendre.

0voto

APC Points 69630

La meilleure façon de s'assurer qu'un enregistrement parent ne puisse pas être inséré sans un enfant est d'utiliser la syntaxe INSERT ALL. Cela nous permet d'insérer des enregistrements dans plusieurs tables dans la même instruction.

    INSERT ALL
        INTO parent
             (pk\_col, val1, val2)
        INTO child1 
             (pk\_col, val3, val4)
    SELECT some\_seq.nextval as pk\_col
           , val1
           , val2
           , val3
           , val4
    FROM where\_ever

La table WHERE_EVER peut être une table de staging (peut-être externe). Dans votre cas, ce serait DUAL avec les colonnes VAL étant des paramètres de la signature de votre procédure stockée.

Vous ne pourrez pas empêcher un développeur malveillant d'écrire du code qui insère un enregistrement PARENT sans un enregistrement ENFANT. De même, vous ne pouvez pas empêcher l'insertion d'un enregistrement dans CHILD2 en utilisant la clé primaire d'un PARENT qui a déjà un enregistrement CHILD1. Pour cela, je crains que vous ayez besoin d'examens de code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X