【问题标题】:Trigger for checking values for inserting用于检查插入值的触发器
【发布时间】:2018-10-14 02:51:21
【问题描述】:

我有两个表,这里涉及两个不同的模式。

架构服务和表任务 -- 列 ID

schme mona_internal 和 table officius_unos -- 列任务

在列任务表 officius_unos 中插入时我需要触发器来检查表任务中列 id 中是否存在插入值。如果存在则继续插入,不存在则报错。

这里是触发器:

CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA 
    BEFORE INSERT ON OFFICIUS_UNOS 
    FOR EACH ROW 
    DECLARE
        task_provera number(10);

        BEGIN
            select id into task_provera from servis.task
            where id=:new.task;

            if (task_provera is null)
                then raise_application_error(-20101, 'No task');
                else insert into mona_internal.OFFICIUS_UNOS (task) values (:new.task);
            end if;    

        END;

触发器已编译,但尝试在列任务表officius_unos中插入新值时出现问题, 它返回给我这条消息

insert into officius_unos (task) values (291504);

    Error report -
    ORA-00036: maximum number of recursive SQL levels (50) exceeded
    ORA-00036: maximum number of recursive SQL levels (50) exceeded
    ORA-06512: at "MONA_INTERNAL.PROBA_PROBA", line 5
    ORA-04088: error during execution of trigger 'MONA_INTERNAL.PROBA_PROBA'
    ORA-06512: at "MONA_INTERNAL.PROBA_PROBA", line 10

并且值 291504 存在于表任务的列 id 中。

附:也尝试用检查约束来解决这个问题,但是有禁止的子查询。我用来克服我的问题的解决方案在这里

Oracle: Using subquery in a trigger

【问题讨论】:

  • else insert into mona_internal.OFFICIUS_UNOS (task) values (:new.task); 不是必需的。这就是错误的原因
  • 为什么不使用外键约束呢?

标签: plsql triggers


【解决方案1】:

您不需要在插入触发器中插入。

如果触发器成功,Oracle 将自行继续执行 INSERT。

所以直接的解决方案是从触发器中删除 INSERT:

CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA 
    BEFORE INSERT ON OFFICIUS_UNOS 
    FOR EACH ROW 
DECLARE
    task_provera number(10);
BEGIN
    select id 
       into task_provera 
    from servis.task
    where id=:new.task;

    if (task_provera is null) then 
      raise_application_error(-20101, 'No task');
    end if;
    // nothing do do here
END;

但以上内容仍然不正确。如果id 不存在于servis.tak 中,则SELECT 将抛出NO_DATA_FOUND 异常。

对此的一种解决方案是使用始终返回一行的聚合函数。如果没有符合 WHERE 条件的行,则返回 NULL 值:

CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA 
    BEFORE INSERT ON OFFICIUS_UNOS 
    FOR EACH ROW 
DECLARE
    task_provera number(10);
BEGIN
    select max(id) 
       into task_provera 
    from servis.task
    where id=:new.task;

    if (task_provera is null) then 
      raise_application_error(-20101, 'No task');
    end if;
    // nothing do do here
END;

或者你可以明确地捕捉到异常:

CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA 
    BEFORE INSERT ON OFFICIUS_UNOS 
    FOR EACH ROW 
DECLARE
    task_provera number(10);
BEGIN
    select max(id) 
       into task_provera 
    from servis.task
    where id=:new.task;

    if (task_provera is null) then 
      raise_application_error(-20101, 'No task');
    end if;

EXCEPTION 
   WHEN NOT_DATA_FOUND THEN 
      raise_application_error(-20101, 'No task');
END;

但正确的方法是使用外键约束。

alter table mona_internal.PROBA_PROBA 
   add constraint fk_proba_task
   foreign key (task)
   references servis.task (id);

那么你根本不需要触发器。

这要求用户mona_internal不仅被授予对servis.task的SELECT权限,而且还被授予references权限:

为此,请以SERVIS 用户身份运行以下命令:

grant references on task to mona_internal;

【讨论】:

  • 伙计,非常感谢您的详细解释,您为我澄清了一个新事物,这让我很困扰
猜你喜欢
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 1970-01-01
  • 2016-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多