【问题标题】:How to implement AFTER INSERT Trigger in Oracle PL/SQL?如何在 Oracle PL/SQL 中实现 AFTER INSERT 触发器?
【发布时间】:2020-12-23 13:41:05
【问题描述】:

我正在尝试在 PLSQL 中实现插入触发器后。
目标是检查每个客户端是否有多个 (>1) 行具有特定状态。
如果是这样,我想提出一个例外并将插入回滚。

我正在努力实现无警告查询,这会在插入过程中导致错误。
我该如何管理这个?
这是我实现的触发器,我猜它需要一些更改。

CREATE TRIGGER blatrigger
    AFTER INSERT
    ON BLATABLE
    REFERENCING NEW AS NEW OLD AS OLD
    FOR EACH ROW
DECLARE
    exception_name EXCEPTION;
    PRAGMA EXCEPTION_INIT (exception_name, -20999);
BEGIN
    if (select count(*) as counter from BLATABLE where CLIENTID = :NEW.CLIENTID and STATUS='PENDING').counter > 1
    THEN
        raise exception_name;
    END IF;
END;

这是表格本身:

create table BLATABLE
(
    ID            NUMBER(19) not null primary key,
    CLIENTID     NUMBER(10),
    CREATED       TIMESTAMP(6),
    STATUS        VARCHAR2(255 char)
);

【问题讨论】:

  • 本地声明的异常的目的是什么?调用者只会得到一个没有任何信息的匿名 ORA-20999 错误。最好raise_application_error(-20999, 'Some message describing the problem including diagnostic details');
  • 顺便说一句,REFERENCING NEW AS NEW OLD AS OLD 没有做任何事情。除非您想使用一些非默认标签名称来引用事物,否则我会完全忽略它。
  • @WilliamRobertson 是的,你的评论很有道理。一开始我只是试着让触发器工作。

标签: sql oracle plsql triggers sql-insert


【解决方案1】:

通过删除 FOR EACH ROW 并转换为您的代码,使用语句级别触发器,而不是行级别:

CREATE OR REPLACE TRIGGER blatrigger
  AFTER INSERT ON BLATABLE
  REFERENCING NEW AS NEW OLD AS OLD

DECLARE
  counter        INT;
  exception_name EXCEPTION;
  PRAGMA EXCEPTION_INIT(exception_name, -20999);
BEGIN
  SELECT MAX(COUNT(*))
    INTO counter
    FROM BLATABLE
   WHERE STATUS = 'PENDING'
   GROUP BY CLIENTID;
     
  IF counter > 1 THEN
    RAISE exception_name;
  END IF;
END;
/

在哪里

  • SELECT 语句需要从IF .. THEN 条件中删除
  • 最有可能的是,对于 Row Level Trigger 情况会引发变异表错误

Demo

【讨论】:

  • 好。但是,由于 GROUP BY 子句,目前 select 可能会引发 TOO_MANY_ROWS。
  • 是的,你是对的@TonyAndrews,谢谢,我需要在那里用 MAX() 包装......
  • TRIGGER 的创建进展顺利,但每当我尝试将数据插入表中时,都会出现警告:[4091] ORA-04091:表 BLATABLE 正在变异,触发器/函数可能看不到它 [2020- 09-04 14:01:16] ORA-06512:在“blatrigger”第 6 行 [2020-09-04 14:01:16] ORA-04088:执行触发器“blatrigger”时出错 [2020-09-04 14:01:16] 位置:17
  • 你是对的。我只是错过了删除 FOR EACH ROW。
  • 是的,我错过了 clientid @MikheilZhghenti 你是对的,现在被替换了,看看答案中的新演示。
【解决方案2】:

目标是检查每个客户端是否有多个 (>1) 行具有特定状态。如果是这样,我想提出一个例外并将插入回滚。

不需要触发器。看起来像一个简单的唯一约束应该在这里完成工作:

create table blatable (
    id            number(19) not null primary key,
    clientid      number(10),
    created       timestamp(6),
    status        varchar2(255 char),
    constraint blaconstraint unique (clientid, status)
);

唯一约束可防止(clientid, status) 在整个表中出现重复。如果 DML 操作(插入、更新)尝试生成副本,则会引发错误并回滚操作。

另一方面,如果您希望每个用户只允许一个“PENDING”状态,那么您可以使用唯一索引,如下所示:

create unique index bla_index
    on blatable( (case when status = 'PENDING' then clientid end) );

【讨论】:

  • 其实没有。因为状态可能不同——不仅仅是“待定”。但我只需要限制“待定”一个。
猜你喜欢
  • 1970-01-01
  • 2013-03-29
  • 2017-10-04
  • 1970-01-01
  • 1970-01-01
  • 2020-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多