2017-04-23 70 views
1

我在Oracle中有4個表格:hotel,touriststay,。 stay表涉及住宿在酒店的遊客,leave表存儲遊客離開酒店的日期的信息。同時觸發器會導致插入和更新困難

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) 
); 

我感興趣的檢查,如果在hotel插入或更新的tenants_amount與表stay的內容相一致,所以我寫了這個觸發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; 
/

我還寫了一第二個觸發器用於存儲leave的信息和tenants_amount屬性的管理hotel。這觸發火災時,DML操作上stay做,因爲它是代表了這種關係

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; 
/

最後,我嘗試插入一些行的表:

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

而且我從酒店觸發錯誤:

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' 

這是發生了什麼:當stay觸發器觸發它試圖提高酒店的tenants_amountid=1,這是一個更新,觸發hotel的觸發器。 hotel的觸發器會檢查tenants_amount是否與stay的內容一致,但所做的更改仍不可見並且未找到任何行。這意味着tenants_amount應爲0,但hotel上的更新將其設置爲1.

我想知道如何解決此問題。

+0

樣式注意:你應該寫三個不同的觸發,在'INSERT','UPDATE'和'DELETE',而不是單一的一個與'IF INSERTING' - 人。內。 – kmkaplan

+0

除非或直到證明在檢索過程中計算值時出現性能問題,否則通常最好*不是*存儲可以計算*的數據。通過這種方式,您可以確保數據總是準確無需創建任何觸發器。 (或者換句話說,它是存儲可計算數據的*存儲*引入*出現不一致的機會) –

回答

0

使觸發stay_trgAFTER INSERT而不是BEFORE INSERT