2010-11-01 2 views
17

J'ai donc essayé de résoudre ce problème, mais il semble que la dernière ligne (la vérification) n'autorise pas les sous-requêtes. Toute façon de faire ce travail Oracle?Utilisation de la sous-requête dans une instruction Check dans Oracle

CREATE TABLE Tank (
    n_id   int, 
    day    date, 
    level   int, 
    CONSTRAINT pk_w_td PRIMARY KEY (n_id,day), 
    CONSTRAINT fk_w_td_tan FOREIGN KEY (n_id) REFERENCES Tanks ON DELETE CASCADE, 
    CHECK (level > 0 AND level <= (SELECT capacity FROM Tanks WHERE Tanks.n_id = TanksDay.n_id)) 
); 

Voici les informations d'erreur:

Error at Command Line:7 Column:32 Error report: SQL Error: ORA-02251: subquery not allowed here 
02251. 00000 - "subquery not allowed here" 
*Cause: Subquery is not allowed here in the statement. 
*Action: Remove the subquery from the statement. 
+1

excellente question. Les contraintes inter-tables générales (en plus des contraintes FK) sont l'une des fonctionnalités que j'aimerais le plus voir ajoutées à Oracle. –

Répondre

22

Il existe trois méthodes de base pour résoudre ce type de problème, car les contraintes CHECK ne peuvent pas être basées sur une requête.

Option 1: Déclenche

L'approche la plus simpliste serait de mettre un déclencheur sur le réservoir et que les requêtes RÉSERVOIRS lève une exception si le niveau dépasse la capacité. Le problème avec ce genre d'approche simpliste, cependant, est qu'il est presque impossible de gérer correctement les problèmes de concurrence. Si la session 1 réduit la CAPACITÉ, alors la session 2 augmente le NIVEAU, et ensuite les deux transactions s'engagent, les déclencheurs ne seront pas capables de détecter la violation. Cela peut ne pas être un problème si l'une des tables ou les deux sont rarement modifiées, mais en général cela va poser un problème.

Option 2: Les vues matérialisées

Vous pouvez résoudre le problème de la concurrence en créant un ON COMMIT vue matérialisée qui rejoint la table du réservoir et RÉSERVOIRS puis créer une contrainte CHECK sur la vue matérialisée qui vérifie que le niveau < = CAPACITÉ. Vous pouvez également éviter de stocker les données deux fois en faisant en sorte que la vue matérialisée contienne uniquement des données qui violeraient la contrainte. Cela nécessitera des journaux d'affichage matérialisés sur les deux tables de base, ce qui ajoutera un peu de surcharge aux insertions (bien que moins que l'utilisation de déclencheurs). Pousser la vérification à commit-time résoudra le problème de la concurrence, mais cela introduit un peu de problème de gestion des exceptions puisque l'opération COMMIT peut maintenant échouer car l'actualisation de la vue matérialisée a échoué. Votre application devrait être capable de gérer ce problème et d'alerter l'utilisateur de ce fait.

Option 3: Changer le modèle de données

Si vous avez une valeur dans le tableau A qui dépend d'une limite dans le tableau B, qui peut indiquer que la limite B doit être un attribut de la table A (au lieu de ou en plus d'être un attribut de la table B). Cela dépend des spécificités de votre modèle de données, bien sûr, mais cela vaut souvent la peine d'être considéré.

7

Non malheureusement contraintes CHECK ne peuvent pas contenir les sous-requêtes - voir documentation.

0

Vous aurez probablement besoin de créer des déclencheurs et d'utiliser RAISE_APPLICATION_ERROR si elle est en dehors de la plage autorisée.

+0

Et une autre chose à considérer lors de la création d'un déclencheur, c'est que vous pourriez vouloir un déclencheur sur la capacité de maintien de la table pour s'assurer qu'il n'a jamais été rendu plus petit que le niveau maximum. –

1

La réponse de Justin a de bonnes idées. Un autre consiste à envelopper tous les insertions/mises à jour de la table avec un paquet (un TAPI, si vous voulez), et mettre en œuvre les contrôles là. Vous devez vous assurer que toutes les applications utilisent votre TAPI. Vous devrez également implémenter un verrouillage personnalisé pour protéger la contrainte contre les effets de l'activité simultanée.

Questions connexes