ORA01400的自定义异常无效

时间:2014-04-28 20:01:13

标签: sql oracle exception-handling triggers

我正在完成学校作业,我必须在所有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在这种情况下可能没有用,但这与此练习无关)

2 个答案:

答案 0 :(得分:0)

试试这个:

它应该是这样的:

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;

答案 1 :(得分:0)

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

您可以在触发器中手动和显式地检查每个不可为空的列,并在任何为空时引发您自己的异常,但您实际上只是复制内置功能。并且假设您跟踪列定义 - 如果更改列的可为空性,则必须记住更新触发器以匹配,除非您想要在运行时动态检查列 - 这将是如果可能的话,最好是狡猾的(cust_id)。

如果您的作业实际上并不需要这种自定义处理,那么似乎需要付出很多努力才能获得真正的收益。 Oracle recommend you don't use triggers when you can use constraints;我知道这是一个练习,但即便如此,这也不是你想要做的事情。


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

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 sequence上限设置在当前最高值之上,并确保设置maxvalue;然后导致错误,并在完成测试后将其重置为高值。

另外,另外,如果您使用11g,则有两种方法可以将序列号分配给列;在11g之前你必须按照你所示的那样去做:

nocycle

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

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