PL/SQL Oracle 错误处理

Posted

技术标签:

【中文标题】PL/SQL Oracle 错误处理【英文标题】:PL/SQL Oracle Error Handling 【发布时间】:2013-03-21 12:54:48 【问题描述】:

我创建了一个触发器,它只允许用户拥有 10 个当前已下订单。因此,现在当客户尝试下订单号 11 时,oracle 数据库会返回一个错误。那么3个错误。

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;

非常感谢 理查德

【问题讨论】:

另一个不喜欢触发器的原因。你能把你的插入语句包装在一个包/过程调用中,把它困在那里,然后向衣领返回一条错误消息吗?您没有说明此语句从哪个编程环境执行。你能过滤调用应用程序中的错误吗? 我确信这在其他地方得到了(很好)的回答......我只是找不到它:-(。 Sigh...:正如我所说,您的应用程序需要捕获异常。例如,如果您使用 OCI 在 php 中编写,您可以使用 oci_error 函数并查看指定的 ORA-20000 错误 [I] 是否出现。 【参考方案1】:

异常传播从内部到外部块,而不是从外部到内部块的变量范围。有关这方面的更多参考,请阅读 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的异常栈的本质。从最内层到最外层的所有错误都会被引发。这在很多时候对我们确定错误的确切来源很有帮助。

【讨论】:

嗯我明白你的意思,但我只是在 oracle sql 命令中使用 sql 代码 是的,我明白了,但是您的 SQL 命令执行一个触发器,这就是整个异常堆栈出现的地方。 第一个可从前端捕获的错误是您使用raise_application_error 在触发器中引发的错误。你可以忍受,至少,我想。全世界都这样! 您好,感谢您抽出宝贵时间,我已经添加了触发代码,您可以向我解释如何更改错误吗?再次感谢 请看我回答的编辑部分。【参考方案2】:

在触发器中可以添加异常处理部分,如下图:

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

【讨论】:

【参考方案3】:

我看到这是一篇相当老的帖子,但我认为读者应该意识到这一点

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

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

事实上,业务规则基本上只能在提交时执行;但在 Oracle 数据库中没有像 ON-COMMIT 触发器这样的东西。 您可以做的是将记录计数非规范化到客户表中(添加列 ORDER_COUNT),并在该表中放置一个延迟约束(ORDER_COUNT

一个完全可靠但有点麻烦的替代方法是创建一个物化视图(类似于SELECT fk_customer_id, count(*) order_count from placed_orders group by fk_customer_id,在placed_order 表上使用FAST REFRESH ON COMMIT,并在物化视图上创建一个检查约束order_count

【讨论】:

以上是关于PL/SQL Oracle 错误处理的主要内容,如果未能解决你的问题,请参考以下文章

错误恢复 PL/SQL oracle 中的下一种错误处理类型

PL/SQL 异常错误处理

总结:整理 oracle异常错误处理 . (转载)

使用 pl/sql 过程记录错误和处理异常

通过 PL/SQL 手动处理交互式网格并不断抛出错误

Oracle存储过程的异常处理