2017-04-23 2 views
1

J'ai 4 tables dans Oracle: hotel, tourist, stay, leave. Le tableau stay fait référence à un touriste séjournant dans un hôtel, et la table leave stocke les informations de la date à laquelle un touriste quitte un hôtel.Les déclenchements simultanés rendent difficiles les insertions et les mises à jour

CREATE TABLE hotel (
    id NUMBER(5), 
    name VARCHAR2(50), 
    tenants_amount NUMBER(3) 
); 

ALTER TABLE hotel ADD CONSTRAINT hotel_c1 
CHECK(tenants_amount>=0 AND tenants_amount<=100); 

CREATE TABLE tourist (
    id NUMBER(5), 
    name VARCHAR2(50) 
); 

CREATE TABLE stay (
    tourist_id NUMBER(5), 
    hotel_id NUMBER(5) 
); 

CREATE TABLE leave (
    departure_date DATE, 
    hotel_id NUMBER(5), 
    tourist_id NUMBER(5) 
); 

Je suis intéressé par vérifier si tenants_amount d'un insert ou mise à jour sur hotel est compatible avec le contenu de la table stay, donc j'écrit ce déclencheur pour hotel

CREATE OR REPLACE TRIGGER hotel_trg 
BEFORE INSERT OR UPDATE ON hotel 
FOR EACH ROW 
DECLARE 
    amount NUMBER(3); 
BEGIN 
    SELECT COUNT(tourist_id) INTO amount FROM stay WHERE hotel_id=:NEW.id GROUP BY hotel_id; 
    IF :NEW.tenants_amount!=amount THEN 
     RAISE_APPLICATION_ERROR(-20001, 'Specified tenants amount differs from the system records'); 
    END IF; 
EXCEPTION 
    WHEN NO_DATA_FOUND THEN 
     IF :NEW.tenants_amount!=0 THEN 
      RAISE_APPLICATION_ERROR(-20001, 'Specified tenants amount differs from the system records'); 
     END IF; 
END; 
/

J'ai aussi écrit un deuxième déclencheur pour stocker les informations de leave et la gestion de l'attribut tenants_amount de hotel. Ce feu de déclenchement lorsque les opérations DML sont faites sur stay parce qu'il est le tableau qui représente cette relation

CREATE OR REPLACE TRIGGER stay_trg 
BEFORE INSERT OR UPDATE OR DELETE ON stay 
FOR EACH ROW 
DECLARE 
    amount NUMBER(3); 
BEGIN 
    IF INSERTING THEN 
     SELECT tenants_amount INTO amount FROM hotel WHERE id=:NEW.hotel_id; 
     UPDATE hotel SET tenants_amount=amount+1 WHERE id=:NEW.hotel_id; 
    END IF; 
    IF UPDATING AND :NEW.hotel_id!=:OLD.hotel_id THEN 
     SELECT tenants_amount INTO amount FROM hotel WHERE id=:OLD.hotel_id; 
     UPDATE hotel SET tenants_amount=amount-1 WHERE id=:OLD.hotel_id; 
     INSERT INTO leave VALUES (SYSDATE, :OLD.hotel_id, :OLD.tourist_id); 
     SELECT tenants_amount INTO amount FROM hotel WHERE id=:NEW.hotel_id; 
     UPDATE hotel SET tenants_amount=amount+1 WHERE id=:NEW.hotel_id; 
    END IF; 
    IF DELETING THEN 
     SELECT tenants_amount INTO amount FROM hotel WHERE id=:OLD.hotel_id; 
     UPDATE hotel SET tenants_amount=amount-1 WHERE id=:OLD.hotel_id; 
     INSERT INTO leave VALUES (SYSDATE, :OLD.hotel_id, :OLD.tourist_id); 
    END IF; 
END; 
/

Enfin, j'essaie d'insérer quelques lignes:

INSERT INTO hotel VALUES (1,'Hotel 1',0); 
INSERT INTO tourist VALUES (1, 'Tourist 1'); 
INSERT INTO stay VALUES (1, 1); 

Et je reçois l'erreur de la détente de l'hôtel :

ERROR at line 1: 
ORA-20001: Specified tenants amount differs from the system records 
ORA-06512: at "HOTEL_TRG", line 11 
ORA-04088: error during execution of trigger 'HOTEL_TRG' 
ORA-06512: at "STAY_TRG", line 6 
ORA-04088: error during execution of trigger 'STAY_TRG' 

C'est ce qui se passe: lorsque le déclencheur de stay est tiré, il tente d'augmenter le tenants_amount de l'hôtel avec id=1, il s'agit d'une mise à jour qui déclenche le déclenchement de hotel. Le déclencheur de hotel vérifie si le tenants_amount est cohérent avec le contenu de stay mais les modifications ne sont toujours pas visibles et il ne trouve aucune ligne. Cela signifie que le tenants_amount devrait être 0 mais la mise à jour sur hotel l'a mis à 1.

Je veux savoir comment puis-je résoudre ce problème.

+0

note Style: vous devez écrire trois déclencheur distinct, sur 'INSERT',' update' et 'delete', au lieu d'un seul avec un' IF INSERTING' - Al. à l'intérieur. – kmkaplan

+0

Il est généralement préférable de ne pas * stocker * les données pouvant être * calculées *, à moins qu'il ne soit démontré que le calcul de la valeur lors de la récupération pose un problème de performances. De cette façon, vous vous assurez que les données sont * toujours * précises sans avoir à créer de déclencheurs. (Ou, pour le dire autrement, c'est le * stockage * des données calculables qui * introduit * la possibilité que l'incohérence apparaisse) –

Répondre

0

Faire la gâchette stay_trgAFTER INSERT au lieu de BEFORE INSERT