2013-03-21 59 views
1

我创建了一个只允许用户有10个当前下订单的触发器。所以现在当客户试图放置第11号订单时,oracle数据库会抛出一个错误。那么3错误。PL/SQL Oracle错误处理

ORA-20000:您目前有10个或更多的订单处理。

ORA-06512:在 “C3283535.TRG_ORDER_LIMIT”,第12行

ORA-04088:触发的执行期间错误 'C3283535.TRG_ORDER_LIMIT'

顶端错误是一个我已经创建使用:

raise_application_error(-20000,'您目前有10个或更多订单 处理'。

我只是想知道搜索后,并尝试了很多方法如何更改其他两个错误的错误信息,甚至不显示它们都一起给用户?

这里是我使用的代码

create or replace trigger trg_order_limit 
    before insert on placed_order for each row 
    declare 
    v_count number; 
    begin 
    -- Get current order count 
    select count(order_id) 
    into v_count 
    from placed_order 
    where fk1_customer_id = :new.fk1_customer_id; 

    -- Raise exception if there are too many 
    if v_count >= 10 then 
    EXCEPTION 
    WHEN OTHERS THEN 
    raise_application_error(-20000, 'You currently have 10 or more orders processing.'); 
    end if; 
    end; 

非常感谢 理查德

+0

不喜欢触发器的另一个原因。你可以用你的insert语句包装一个程序包/程序调用,将其捕获并返回一个错误信息给领子?您不会说明该语句执行的编程环境。你可以过滤调用应用程序中的错误吗? – OldProgrammer 2013-03-21 13:05:15

+0

我确信这个问题在其他地方得到了解答...我找不到它:-(。 – Ben 2013-03-21 13:07:12

+1

[叹气...](http://stackoverflow.com/a/15546744/876937):As我说过,异常需要被你的应用程序所捕获,例如,如果你使用OCI编写PHP,你可以使用'oci_error'函数,看看'ORA-20000'错误[I]是否指定 – Xophmeister 2013-03-21 13:07:19

回答

4

的异常传播进入从内部到外部的块,而不是从外部进入的变量范围到内部块。关于这方面的更多参考,请阅读McLaughlin的“用PL/SQL编程”,第5章。

你在这里得到的是一个异常堆栈 - 从最内层块到最外层块引发的异常。

当您从触发器中引发异常时,您的raise_application_error语句返回错误。

然后它被传播到说ORA-06512: at "C3283535.TRG_ORDER_LIMIT", line 12的触发块。这是因为触发器将引发的异常视为错误并停止继续。

该错误然后传播到引发ORA-04088: error during execution of trigger 'C3283535.TRG_ORDER_LIMIT'的会话。这个错误会向我们报告错误在哪里发生,如程序的哪一部分。

如果您使用的是Java Server Pages或PHP等前端程序,您将首先发现引发的错误--20000。所以,你可以显示给你的最终用户。

编辑:

关于第一个错误 - ORA-20000,您可以在RAISE_APPLICATION_ERROR语句本身改变它。

如果您想要处理ORA-06512,您可以使用Uday Shankar的答案,这有助于处理此错误并显示相应的错误消息。

但是,你仍然会得到最后的ORA-04088。如果我在你的位置,我不会担心,因为在获得ORA-20000之后,我会在前端自己提出应用程序错误,同时隐藏用户的所有其他详细信息。

其实这个这个oracle的异常栈的本质。从最内层到最外层的所有错误都会被提升。这对我们确定错误的确切来源很有帮助。

+0

嗯我明白你的意思,但我只是在oracle sql命令中使用sql代码 – 2013-03-21 13:15:10

+0

是的,我明白了,但是你的SQL命令执行了一个触发器,这就是整个异常堆栈进入队伍的地方。 – Rachcha 2013-03-21 13:16:16

+2

第一个错误提出从你的前端可以捕捉到的是你在触发器中使用'raise_application_error'引发的错误。至少,我认为你至少可以忍受,整个世界都可以! – Rachcha 2013-03-21 13:18:19

2

在触发器可以添加例外处理部分如下图所示:

EXCEPTION 
    WHEN OTHERS THEN 
     raise_application_error(-20000, 'You currently have 10 or more orders processing.'); 
0

我看,这是一个很老的文章,但我认为读者应该知道,

  1. 这并没有真正执行业务规则(最多10个订单)。如果 是只是“一些”号码,以避免过高的金额,你不要 如果有时人们有12个订单,那么这可能是好的。但是,如果没有,请考虑已经有9个订单的场景,然后同时为2个不同的会话/交易插入同一个客户的订单。在这种情况下,您最终将获得11个订单,而不会检测到这种溢出情况。所以你不能依靠这个触发器。
  2. 除此之外,如果可以更新fk1_customer_id,则可能还需要在更新时触发此触发器(我已经看到了实现方式,首先将NULL放入FK列中,稍后更新为实际值) 。您可能需要考虑这种情况是否现实。
  3. 触发器存在根本缺陷。你在一个事务中,并且在一个当前正在执行但尚未完成的语句中。那么如果插入不是单行插入,而是像 insert into placed_order (select ... from waiting_orders ...) 那你期望触发器看到什么?

这种业务规则不易实施。但是,如果您选择在触发器中执行此操作,最好在after语句触发器中执行此操作(因此,不要在before行触发器中执行)。 after语句触发器仍然不会看到其他未提交事务的结果,但至少当前语句处于已定义状态。

事实上,业务规则基本上只能在提交时执行;但在Oracle数据库中没有像ON-COMMIT触发器那样的东西。 您可以执行的操作是将记录数记录到客户表中(添加一列ORDER_COUNT),并在该表中放置一个延迟约束(ORDER_COUNT < = 10)。 但是,你仍然依靠在你的代码中正确保持这个字段。

一个完全可靠的替代,但稍显麻烦,是对物化视图的placed_order表中创建一个物化视图(类似于SELECT fk_customer_id, count(*) order_count from placed_orders group by fk_customer_id,与FAST REFRESH ON COMMIT并创建一个检查约束ORDER_COUNT < = 10。 这是一个关于该唯一方式来可靠地执行这种类型的约束,而不必考虑并发会话,更新等所有可能的情况。 但请注意,FAST REFRESH ON COMMIT会减慢提交速度;所以此解决方案不适用于大容量(叹息...为什么只是Oracle不提供ON COMMIT触发器...)