【问题标题】:Simultaneous triggers makes inserts and updates difficult同时触发器使插入和更新变得困难
【发布时间】:2017-09-19 23:01:30
【问题描述】:

我在 Oracle 中有 4 个表:hoteltouriststayleavestay 表涉及游客入住酒店,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)
);

我有兴趣检查tenants_amount 上的插入或更新hotel 是否与表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的信息和hoteltenants_amount属性的管理。当对 stay 进行 DML 操作时会触发此触发器,因为它是代表此关系的表

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_amount 增加到id=1,这是一个触发hotel 触发器的更新。 hotel 的触发器检查tenants_amount 是否与stay 的内容一致,但更改仍然不可见,也没有找到任何行。这意味着 tenants_amount 应该为 0,但 hotel 的更新将其设置为 1。

我想知道如何解决这个问题。

【问题讨论】:

  • 样式说明:您应该在INSERTUPDATEDELETE 上编写三个不同的触发器,而不是一个带有IF INSERTING 的单个触发器- al。里面。
  • 最好存储可计算的数据,除非或直到证明在检索期间计算值存在性能问题.这样,您可以确保数据始终准确,而无需创建任何触发器。 (或者换句话说,存储可计算数据引入出现不一致的机会)

标签: oracle triggers simultaneous mutating-table


【解决方案1】:

触发stay_trg AFTER INSERT 而不是BEFORE INSERT

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-08
    相关资源
    最近更新 更多