Oracle:在批量收集期间检测类型构造函数中的异常

Oracle: detect exception in type construtor during bulk collect

环境:Oracle 11g 我有一个类型 ty1,带有一些参数,例如 s1、s2。

如果我这样使用它:

SELECT ty1(s1,s2) BULK COLLECT INTO l_collection_of_ty1 FROM ...

我得到一个 collection 的 ty1。

现在,如果在 ty1 的构造函数调用之一中引发异常,我的 collection 的相应元素将设置为 NULL,但整体 SELECT 有效(无例外,collection返回)。

我的问题是,我可以在 SELECT 之后立即检测到这一点,而不必遍历 collection 吗?有没有一种方法可以像 SQL%BULK_EXCEPTION DML 那样访问原始错误消息?

我想到的一个解决方法是在 BULK COLLECT 期间不使用构造函数,而是读取 s1 和 s2 的 collections,然后在我自己的循环中构造 TYPE,我可以在其中处理异常,但是代码要多得多,如果 Oracle 有一些内置方式,我会更喜欢它。

我为你写了一个小测试用例,必须在 ty1 中吞下异常而不引发异常,否则 select 将无法成功完成:

create or replace function p1 (v1 number) return number
as
begin
if v1 = 1 then
return 1;
elsif v1 = 2 then
raise_application_error(-20010,'err 2');
else
raise_application_error(-20010,'err 3');
end if;
end;
/

declare
type type1 is table of number;
col1 type1;
begin
select p1(level) bulk collect into col1 from dual connect by level <=3;
end;
/

结果:

Error report -
ORA-20010: err 2

所以我给你的建议是 - 如果你想接近你的解决方案 - 在你处理 ty1 中的异常的地方,你将异常写入 table。然后,您可以访问此 table 来查找异常,而无需遍历整个集合。但老实说,在 PL/SQL 循环遍历集合有什么问题,它都在内存中? HTH

我只看到两个选项。

  1. 在构造函数中消耗了一个异常。
  2. 正在使用另一个构造函数。

示例:

create or replace type ty1 as object (p1 number
, constructor function  ty1 (p1 varchar2) 
    return self as result);

create or replace type body ty1 
is 
  constructor function ty1 (p1 varchar2) 
    return self as result is
    x number;
  begin
   raise_application_error(-20000,'Always Exception');
   return;
  end;
end;

测试 1 无异常:

declare
 type l_collection_of_ty1 is table of ty1;
 a varchar2(4000);
 x   l_collection_of_ty1;
begin 

--test1   select ty1(level) bulk collect into x from dual connect by level < 10;
     -- no exceptions 
 --test2  select ty1(level||1) bulk collect into x from dual connect by level < 10; 
       -- exceptions

  --test3 select ty1(null) bulk collect into x from dual connect by level < 10; 
       -- exceptions
end; 

在 Test1 中,数据库正在使用属性值构造函数。默认生成。
在 Test2 中,db 使用用户定义的构造函数。
在 Test3 中,db 无法找出应该使用哪个构造函数。()

这里是一个测试用例,它演示了所有异常都是通过批量抛出的 -select,但是 NO_DATA_FOUND 被丢弃了。

-- remember to retreive dbms_output
SET SERVEROUTPUT ON

CREATE OR REPLACE FUNCTION p1 (v1 PLS_INTEGER)
   RETURN NUMBER
AS
BEGIN
   CASE v1
      WHEN 1
      THEN
         RAISE ACCESS_INTO_NULL;
      WHEN 2
      THEN
         RAISE CASE_NOT_FOUND;
      WHEN 3
      THEN
         RAISE COLLECTION_IS_NULL;
      WHEN 4
      THEN
         RAISE CURSOR_ALREADY_OPEN;
      WHEN 5
      THEN
         RAISE DUP_VAL_ON_INDEX;
      WHEN 6
      THEN
         RAISE INVALID_CURSOR;
      WHEN 7
      THEN
         RAISE INVALID_NUMBER;
      WHEN 8
      THEN
         RAISE LOGIN_DENIED;
      WHEN 9
      THEN
         RAISE NO_DATA_FOUND;
      WHEN 10
      THEN
         RAISE NOT_LOGGED_ON;
      WHEN 11
      THEN
         RAISE PROGRAM_ERROR;
      WHEN 12
      THEN
         RAISE ROWTYPE_MISMATCH;
      WHEN 13
      THEN
         RAISE SELF_IS_NULL;
      WHEN 14
      THEN
         RAISE STORAGE_ERROR;
      WHEN 15
      THEN
         RAISE SUBSCRIPT_BEYOND_COUNT;
      WHEN 16
      THEN
         RAISE SUBSCRIPT_OUTSIDE_LIMIT;
      WHEN 17
      THEN
         RAISE SYS_INVALID_ROWID;
      WHEN 18
      THEN
         RAISE TIMEOUT_ON_RESOURCE;
      WHEN 19
      THEN
         RAISE TOO_MANY_ROWS;
      WHEN 20
      THEN
         RAISE VALUE_ERROR;
      WHEN 21
      THEN
         RAISE ZERO_DIVIDE;
      ELSE
         RETURN v1;
   END CASE;
END;
/

DECLARE
   TYPE type1 IS TABLE OF NUMBER;

   col1   type1;
BEGIN
   FOR ii IN 1 .. 22
   LOOP
      BEGIN
         SELECT p1 (ii)
           BULK COLLECT INTO col1
           FROM DUAL;

         IF col1 (1) IS NULL
         THEN
            DBMS_OUTPUT.put_line (TO_CHAR (ii, '00') || ': NULL');
         ELSE
            DBMS_OUTPUT.put_line (TO_CHAR (ii, '00') || ': ' || col1 (1));
         END IF;
      EXCEPTION
         WHEN OTHERS
         THEN
            DBMS_OUTPUT.put_line (
                  TO_CHAR (ii, '00')
               || ': exception '
               || SQLCODE);
      END;
   END LOOP;
END;
/