1

我创建了一个触发器,它只允许用户拥有 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;

非常感谢理查德

4

3 回答 3

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异常栈的本质。从最内层到最外层的所有错误都会被引发。这在很多时候对我们确定错误的确切来源很有帮助。

于 2013-03-21T13:00:44.653 回答
2

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

EXCEPTION
    WHEN OTHERS THEN
        raise_application_error(-20000, 'You currently have 10 or more orders processing.');
于 2013-03-21T13:03:36.693 回答
0

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

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

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

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

一个完全可靠但有点麻烦的替代方法是创建一个物化视图(类似于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 触发器...)

于 2014-03-21T12:00:16.823 回答