Oracle
 sql >> Base de données >  >> RDS >> Oracle

Utilisation de IF EXISTS (SELECT ...) dans un trigger BEFORE INSERT (Oracle)

Tout d'abord, si vous utilisez SQL*Plus, lorsque vous créez un objet et qu'on vous dit qu'il y a des erreurs de compilation, la commande show errors vous montrera les erreurs.

Si vous avez exécuté show errors , on vous dira que IF EXISTS n'est pas une syntaxe valide. Vous pourriez faire quelque chose comme

SELECT COUNT(*)
  INTO l_cnt
  FROM <<rest of query>>

IF( l_cnt > 0 )
THEN
  RAISE_APPLICATION_ERROR ...
END IF;

Cependant, une fois l'erreur de compilation corrigée, vous vous retrouverez avec des erreurs d'exécution. Dans un déclencheur de niveau ligne sur surveillance , vous ne pouvez généralement pas interroger surveillance (vous pouvez si tout ce que vous faites est un INSERT VALUES qui est garanti de n'insérer qu'une seule ligne). Si vous le faites, vous obtiendrez une erreur de déclenchement de mutation lors de l'exécution.

Du point de vue du modèle de données, lorsque vous vous retrouvez à concevoir une table dans laquelle les données valides pour une ligne particulière dépendent des données stockées dans d'autres lignes de la même table, vous avez généralement enfreint les principes de normalisation et vous êtes généralement mieux servi en corrigeant le modèle de données sous-jacent.

Si vous êtes vraiment déterminé à conserver le modèle de données, je préférerais créer une vue matérialisée qui s'actualise lors de la validation et qui contient uniquement des données pour les lignes qui violent vos critères. Vous pouvez ensuite mettre des contraintes sur cette vue matérialisée qui génèrent des erreurs au moment de la validation lorsque vos critères ne sont pas respectés. Cela nécessitera des journaux de vue matérialisés sur votre table.

Si vous voulez vraiment conserver le modèle de données et que vous souhaitez appliquer la logique avec des déclencheurs, vous aurez besoin de la solution classique à trois déclencheurs (ou d'un déclencheur composé à trois parties si vous utilisez 11.2 ou une version ultérieure). Vous créeriez un package avec une collection de valeurs de clé primaire. Un déclencheur d'instruction avant initialiserait la collection. Un déclencheur au niveau de la ligne insère les clés primaires des lignes qui ont été insérées et/ou mises à jour dans cette collection. Et puis un déclencheur d'instruction after itérerait sur cette collection et implémenterait les vérifications que vous souhaitez. Cela fait beaucoup de pièces mobiles, c'est pourquoi je le déconseille généralement.

De plus, même si vous faites fonctionner tous ces éléments, votre logique ne vous protégera pas dans un environnement multi-utilisateurs. Lorsque plusieurs utilisateurs accèdent au système en même temps, il est tout à fait possible qu'un utilisateur insère une ligne, le deuxième utilisateur insère une autre ligne avec une plage qui se chevauche, puis chaque session sera validée. Dans ce cas, les deux ensembles de déclencheurs autoriseront le changement, mais il vous restera toujours des données dans le tableau qui ne respectent pas vos exigences. La vue matérialisée, puisqu'elle est appliquée au moment de la validation plutôt qu'au moment de l'insertion, fonctionnera correctement dans un environnement multi-utilisateurs. Si vous souhaitez que les déclencheurs fonctionnent dans un environnement multi-utilisateurs, vous devrez les compliquer davantage en ajoutant une logique supplémentaire qui applique une sérialisation qui bloquerait l'insert de la deuxième session. de l'exécution jusqu'à la première session validée ou annulée. Cela ajoute de la complexité, réduit l'évolutivité et, selon la manière dont il est mis en œuvre, peut constituer un cauchemar pour l'assistance.