【问题标题】:Custom exception for ORA01400 not workingORA01400 的自定义异常不起作用
【发布时间】:2014-04-28 20:01:12
【问题描述】:

我正在做一项学校作业,我必须在所有 PL/SQL 中添加例外。由于我创建了一些序列和触发器来添加自动增量功能(这是 Oracle SQL),我想我应该为所有与 ORA-01400: cannot insert NULL into(...) 相关的触发器添加一个例外。

按照example here,我创建了一个包来存储我的自定义异常,因此我可以在所有实现自动增量的触发器中使用它:

CREATE OR REPLACE PACKAGE my_exceptions
AS
    insert_null_into_notnull EXCEPTION;
    PRAGMA EXCEPTION_INIT(insert_null_into_notnull, -1400);
END my_exceptions;
/

这是触发器之一:

CREATE OR REPLACE TRIGGER update_cust_id
BEFORE INSERT ON CUSTOMER
FOR EACH ROW
BEGIN
    SELECT pk_cust_seq.NEXTVAL INTO :NEW.CUST_ID FROM DUAL;
EXCEPTION
    WHEN my_exceptions.insert_null_into_notnull THEN
        DBMS_OUTPUT.PUT_LINE('Attempted to insert a null value into not nullable column.');
        RAISE;
END;
/

问题是当我尝试故意添加失败的插入时,我仍然得到典型的 Oracle 错误:ORA-01400: cannot insert NULL into (...),而不是我想要的 DBMS_OUTPUT。

我看了几次网站上的例子,看不出有什么区别,除了异常是用在过程中而不是触发器中。那是问题吗?如果是这样,我怎样才能使它在触发器中工作?

(OBS。这只是一个演示错误处理的学校练习;DBMS_OUTPUT 在这种情况下可能没有用,但这与本练习无关)

【问题讨论】:

    标签: sql oracle exception-handling triggers


    【解决方案1】:

    问题正如您在评论中提到的那样。在before 触发器触发并完成后,insert 引发 ORA-01400。如果不是这种情况,您将无法从触发器自动设置不可为空的列 - 例如,从序列设置主键值(模仿自动增量 ID),就像您设置 @ 987654324@。在附加到触发器的 PL/SQL 块中没有引发异常。

    您可以在触发器中手动明确地检查每个不可为空的列,如果有任何为空的,则引发您自己的异常,但实际上您只是在复制内置功能。并且假设您跟踪列定义 - 如果您更改列的可空性,您必须记住更新触发器以匹配,除非您认为在运行时动态检查列 - 这将是如果可能的话,充其量是棘手的(使用dbms_sql)。

    如果您的任务实际上不需要这种自定义处理,那么似乎付出了很多努力却没有真正的收获。甲骨文recommend you don't use triggers when you can use constraints;我知道这是一种练习,但即便如此,这也不是你想做的事情。


    在您显示的触发器中强制出现运行时错误的一种方法是操纵序列,以便您尝试超过其MAXVALUE

    CREATE TABLE customer (cust_id NUMBER PRIMARY KEY, name VARCHAR2(20));
    
    CREATE SEQUENCE pk_cust_seq MINVALUE 1 MAXVALUE 3 NOCYCLE;
    
    CREATE OR REPLACE TRIGGER update_cust_id
    BEFORE INSERT ON CUSTOMER
    FOR EACH ROW
    BEGIN
        SELECT pk_cust_seq.NEXTVAL INTO :NEW.CUST_ID FROM DUAL;
    EXCEPTION
        WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('Unable to create cust_id.');
            RAISE;
    END;
    /
    

    那么前三个插入会起作用,第四个会失败:

    set serveroutput on
    insert into customer(name) values ('Customer 1')
    
    1 rows inserted.
    
    insert into customer(name) values ('Customer 2')
    
    1 rows inserted.
    
    insert into customer(name) values ('Customer 3')
    
    1 rows inserted.
    
    insert into customer(name) values ('Customer 4')
    
    Error starting at line : 24 in command -
    insert into customer(name) values ('Customer 4')
    Error report -
    SQL Error: ORA-08004: sequence PK_CUST_SEQ.NEXTVAL exceeds MAXVALUE and cannot be instantiated
    ORA-06512: at "SCHEMA.UPDATE_CUST_ID", line 6
    ORA-04088: error during execution of trigger 'SCHEMA.UPDATE_CUST_ID'
    08004. 00000 -  "sequence %s.NEXTVAL %s %sVALUE and cannot be instantiated"
    *Cause:    instantiating NEXTVAL would violate one of MAX/MINVALUE
    *Action:   alter the sequence so that a new value can be requested
    Unable to create cust_id.
    

    你已经有了序列,所以在你的情况下,你可以alter sequencemaxvalue 限制在你当前的最高值之​​上,并确保设置了nocycle;然后导致错误,并在您完成测试后将其重置为高值。

    另外,顺便说一句,如果您使用的是 11g,则有两种方法可以将序列号分配给列;在 11g 之前,您必须按照您的说明进行操作:

    SELECT pk_cust_seq.NEXTVAL INTO :NEW.CUST_ID FROM DUAL;
    

    ...但现在你可以这样做了:

    :NEW.CUST_ID := pk_cust_seq.NEXTVAL;
    

    【讨论】:

    • 感谢您提供的所有信息。事实上,这个赋值只是指定我所有的 PL/SQL 都应该进行错误处理。检查不正确的空值是我想到的与这些触发器相关的唯一事情。根据我们的结论,我可以在这里提出的唯一错误处理是从序列中获取一个值并将其插入到 cust_id 字段中,但是我想不出那里可能会出现什么问题。我想我只会为 OTHERS 添加一个通用错误处理,上面写着“无法创建 cust_id”。
    • @PrincessLilly - 您绝对可以在触发器中有一个异常处理程序,但它只能捕获该块(或它调用的东西)中引发的异常。您可以强制异常,但只能为此目的向触发器添加额外的代码 - 尝试从字符串设置 ID、伪造数字或日期问题等。捕获 WHEN OTHERS 通常不受欢迎,但至少你正在重新提出错误;您仍然会丢失错误堆栈中的信息。
    • 是的,我同意捕获WHEN OTHERS 并不是您能想到的最好的错误处理方式,但是故意向触发器添加代码也不会给我很多分数。错误处理应该解决由于块之外的情况(我看到的方式)而不是我们故意造成的问题:) 我有另一个想法,也许我会处理这种可能性,由于某种原因,该序列不存在(可能已被错误删除)。这听起来比WHEN OTHERS 酷。
    • @PrincessLilly - 但如果序列不存在,则触发器不会编译;你会在插入时得到一个错误,但这不会来自异常块。除非您通过动态 SQL 分配它,否则这也是一种 hack。也许您可以将序列的maxvalue 设置为较低的值,并设置no cycle - 当您尝试超过插入时的最大值时应该会出现运行时异常?
    • 不,没有必要以错误的方式设置序列。目的是能够解释即使我们正确设置系统也可能发生的实际错误。所以OTHERS 是:) 我相信它不会对我的分数有太大影响。感谢您指出上述关于触发器未编译的内容。
    【解决方案2】:

    试试这个:

    应该是这样的:

    CREATE OR REPLACE TRIGGER update_cust_id
    BEFORE INSERT ON CUSTOMER
    FOR EACH ROW
    BEGIN
      BEGIN
        SELECT pk_cust_seq.NEXTVAL INTO :NEW.CUST_ID FROM DUAL;
      EXCEPTION
        WHEN OTHERS THEN
         IF (SQLCODE=01040) THEN   
           RAISE my_exceptions.insert_null_into_notnull;
        ELSE
           RAISE;
      END;
    
    EXCEPTION
     WHEN my_exceptions.insert_null_into_notnull THEN
       DBMS_OUTPUT.PUT_LINE('Attempted to insert a null value into not nullable column.');
       RAISE;
    END;
    

    【讨论】:

    • 谢谢,但它不起作用。发生同样的问题。此外,您也没有关闭您的“if”语句;-)。我认为问题在于错误是由 INSERT 本身创建的,而不是由触发器执行的操作(它本身不执行插入)
    猜你喜欢
    • 1970-01-01
    • 2013-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多