【问题标题】:Enabling and disabling a trigger inside another trigger在另一个触发器中启用和禁用触发器
【发布时间】:2021-05-27 16:30:48
【问题描述】:

我有一张桌子位置

CREATE TABLE Location (
        idL     INTEGER,
        City    VARCHAR2(15) NOT NULL,
        Street  VARCHAR2(35) NOT NULL,
        Nation  CHAR(6) NOT NULL,
            
        CONSTRAINT PK_idL PRIMARY KEY(idL)
);

还有一张桌子

CREATE TABLE Person(
        p_Name  VARCHAR2(20) NOT NULL,
        p_Surname VARCHAR2(20) NOT NULL,
        idP     INTEGER,
        b_Date  DATE NOT NULL,
        id_PL    INTEGER,
 
        CONSTRAINT PK_idP PRIMARY KEY(idP),
        CONSTRAINT FK_idPL FOREIGN KEY(id_PL) REFERENCES Location(idL)
);

我“自动”计算主键如下:

CREATE SEQUENCE seq_loc_pk
start with 1
increment by 1;

CREATE OR REPLACE TRIGGER auto_pk_loc
BEFORE INSERT ON Location
FOR EACH ROW
BEGIN
    :new.idL := seq_loc_pk.nextval;
END;
/

现在我想为一个新人插入 residence(当然是在我创建了正确的视图之后),而不是像这样的触发器:

CREATE OR REPLACE TRIGGER newperson
INSTEAD OF INSERT ON Residence
FOR EACH ROW
DECLARE
    nl Loc.idL%TYPE;
BEGIN
    ALTER TRIGGER auto_pk_loc DISABLE; -- Error
    nl := seq_loc_pk.nextval;
    :NEW.idL := nl;
    INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation);
    INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,,nl);
    ALTER TRIGGER auto_pk_loc ENABLE;
END;
/

我想过禁用和启用触发器 auto_pk_loc 以便它不会无缘无故地创建额外的值,但我认为这不是正确的方法吗?它是什么?谢谢谁回答。

【问题讨论】:

  • 在另一个触发器中启用一个触发器是行不通的,原因有几个。您能否简单地将if 语句放在auto_pk_loc 触发器中,以便仅在:new.idL is null 时分配一个新值,以便如果任何insert 语句为使用该值的键指定显式值?那将是最简单的选择。
  • 如果相同的位置(城市、街道、国家)已经存在怎么办?您可以在不禁用触发器的情况下将mergewhen not matched then insert 一起使用(但首先您需要有一些唯一的键来匹配)。
  • 假设您禁用了触发器,此时另一个会话将一条记录插入到您的表中 - 它会失败。禁用的触发器被全局禁用。

标签: sql oracle oracle11g triggers


【解决方案1】:

你可以把它放在execute immedaite

BEGIN
    execute immedidate 'ALTER TRIGGER auto_pk_loc DISABLE'; 
    nl := seq_loc_pk.nextval;
    :NEW.idL := nl;
    INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation);
    INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,,nl);
    execute immedidate 'ALTER TRIGGER auto_pk_loc ENABLE';
END;
/

但这会给你带来各种各样的问题; DDL 提交,所以你必须使它成为一个自治事务,你会遇到并发问题。最好避免这种情况。

更好的方法是使用returning 子句来获取你刚刚插入的值:

BEGIN
    INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation)
      returning idl into nl;

    INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,nl);
END;
/

尽管如@astentx 所述,您可能希望使用merge 来避免重复位置。这不支持returing,因此您必须改用insert+update 的某种组合。

最后 - 假设您使用 12c 或更高版本 - 最好使用标识列或序列默认值通过触发器自动生成位置 ID。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-27
    • 2011-09-23
    • 1970-01-01
    • 2013-02-09
    • 2019-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多