我应该在 Oracle Update 语句中使用 Too Many Rows Error 作为例外子句吗?

Posted

技术标签:

【中文标题】我应该在 Oracle Update 语句中使用 Too Many Rows Error 作为例外子句吗?【英文标题】:Should I use the Too Many Rows Error as my exception clause in my Oracle Update statement? 【发布时间】:2012-08-09 04:41:39 【问题描述】:

我有一系列需要在我的 Oracle 包中使用的更新语句。这种情况很少见,但可能偶尔会出现不可避免的用户错误,这会导致更新语句之一引发“单行子查询返回一行或多行”错误。

我一直在研究 oracle PL/SQl 的异常处理,但我对如何以及使用什么来捕获此异常以使包不会崩溃有点困惑。

我知道存在预先构建的“Too Many Rows”异常子句,但我读到的所有内容似乎都说它用于不正确的插入语句。

我可以将此作为我的例外吗?还是我需要建立自己的例外条款。我以前从来没有自己建造过,只有一个粗略的想法,知道在哪里放置所需的一切。

以下代码基本上是在此特定过程中设置更新的方式 但为了简洁起见,我只使用一个简单的例子来说明它的外观。

INSERT INTO TempTable... --(Initial insert statement)

UPDATE TempTable t SET t.Row_one = (SELECT (Statement_One))
WHERE T.Row_One is NULL

UPDATE TempTable t SET t.Row_one = (SELECT (Statement_Two))
WHERE T.Row_One is NULL

UPDATE TempTable t SET t.Row_one = (SELECT (Statement_Three))
WHERE T.Row_One is NULL

-- Does the exception clause start here?
EXCEPTION
    WHEN TOO_MANY_ROWS THEN
(What do I tell the Procedure to do here, what am I able to tell it to do?)

--end of updates that need the exception handling

-- more insert statements into other tables based on data from the preceding Temp Table

END;

这会起作用还是我需要构建一个自定义异常?

提前致谢。

【问题讨论】:

【参考方案1】:

首先,TOO_MANY_ROWS 异常不会捕获您的选择语句返回多行的情况。 TOO_MANY_ROWS 异常适用于 ORA-01422,当您发出返回多行的 SELECT .. INTO 语句时。您将遇到的异常是 ORA-01427,单行子查询返回多行。

如果要在过程中处理此特定错误,请使用 EXCEPTION_INIT pragma 将异常名称与错误相关联:

too_many_values EXCEPTION;
PRAGMA EXCEPTION_INIT(too_many_values, -1427);

然后您可以在异常处理程序中引用此名称:

EXCEPTION
    WHEN TOO_MANY_VALUES THEN
       perform your handler here

您在处理程序中放入的内容取决于您的程序执行的操作。很多时候,您会希望向调用者返回某种错误代码/消息:

PROCEDURE my_proc(p_one VARCHAR2, p_err OUT VARCHAR2) IS
    too_many_values EXCEPTION;
    PRAGMA EXCEPTION_INIT(too_many_values, -1427);
BEGIN
...
EXCEPTION
   WHEN TOO_MANY_VALUES THEN
      p_err := 'More than one value available to assign in the update';
      RAISE;  -- re-raise the exception for the caller

   WHEN OTHERS THEN
      p_err := SQLERRM;  -- return the oracle message for the unexpected error
      RAISE;
END;

另一种方法是跳过特定的异常处理程序并在 WHEN OTHERS 处理程序中返回通用 oracle 消息:

EXCEPTION
  WHEN OTHERS THEN
    p_err := SQLERRM;
END;

第一种方法的优点是,当流程的输出直接反馈给用户时,您可以自定义消息,使其对最终用户更加友好。后一种方法的优点是涉及的编码较少。错误处理是任何应用程序的一个重要方面并且经常被忽略。

Oracle 的文档是here。

编辑:

如果这是一个包,并且你想避免通过一系列过程调用传递一长串错误变量,你可以声明一个包范围的错误变量,遇到错误时设置它,并引发错误再次。

PACKAGE BODY my_pkg is
  g_err  VARCHAR2(256);

PROCEDURE procx(... , p_err OUT VARCHAR2) IS...
  ...
  proc_y(p1);
EXCEPTION
  WHEN OTHERS THEN
    p_err := NVL(g_err, SQLERRM);
END;

PROCEDURE proc_y(p1 VARCHAR2) IS
...
proc_z(p2);

END;

PROCEDURE proc_z(p2 VARCHAR2) IS
  too_many_values EXCEPTION;
  PRAGMA EXCEPTION_INIT(too_many_values, -1427);
BEGIN
  ....
EXCEPTION
   WHEN TOO_MANY_VALUES THEN
      g_err := 'More than one value available to assign in the update';
      RAISE;  -- re-raise the exception for the caller
END;

当 proc_z 中引发异常时,会对其进行处理,然后再次引发。它通过 proc_y(那里没有处理程序)传播回来,然后在 proc_x 中返回给用户。未在全局 g_err 中设置的错误会得到一般的 Oracle 错误消息。这避免了必须在整个包中传递初始错误参数。

【讨论】:

感谢您回复并回答我的所有问题。在您的示例中,我需要将 p_err 声明为正确的输出参数吗?如果这个过程是整个包中的几个过程之一,我还需要通过其他过程一直执行这个参数,以便最终用户看到它是否正确?是否有任何其他可能类型的输出参数可以用来指示此错误? 是的,您需要在整个用户界面调用链中声明错误变量,以将您的特定消息返回给用户。我会用另一种选择来更新我的答案。 非常感谢您提供更多信息。您为我所关心的每一个问题提供了所有答案,我真的认为我现在了解如何处理 Oracle 包中的异常。 非常欢迎,很高兴为您提供帮助。最后一种情况的唯一警告是调用链中的任何异常处理程序都会拦截引发的异常,因此您必须有一个一致的方法来处理整个异常。

以上是关于我应该在 Oracle Update 语句中使用 Too Many Rows Error 作为例外子句吗?的主要内容,如果未能解决你的问题,请参考以下文章

oracle Update语句卡死,详细情况见下文

我如何审计特定用户在 oracle 中特定表上的语句

oracle执行update语句卡住不动

ORACLE/SQL - UPDATE 语句问题(需要 OR 一些集合)

在Oracle触发器中如何执行多条update语句?

oracle pl/sql 循环中以表名作为参数的 UPDATE 语句