存储过程性能:DataNotFound或外部游标

时间:2015-11-18 04:58:11

标签: oracle plsql exception-handling

以下是我的存储过程的小片段。我正在使用Oracle数据库。 我觉得下面不是好方法。首先,我正在检查天气我从查询中得到一行。然后,如果count(CNT2> 0)大于0意味着我从查询中获取记录,那么我再次执行查询以获取列。

这是一种重复的查询。首先,我正在检查计数,然后再次执行查询,如果count大于0.

我的程序中有10个查询,对于每个查询,我首先检查计数然后再次执行查询,如果count大于0.我认为这对于性能观点来说并不好。

我在网上查了一下,发现我们可以通过DATANOTFOUND异常捕获它,并且可以使用外部游标。我是新手,不知道该怎么做。哪一个是性能观点的更好选择。

.............
.............
BEGIN
      oAuditMsg := 'audit : ';
      DBMS_OUTPUT.PUT_LINE('Debug :: inside begin' );

      select count(*) into CNT2  from COMMON_MAPPING where IP like VAR_IP ; 

       IF CNT2 >0 THEN
         select Out_IP,Out_IP1 into VAR_OUTIP, VAR_OUTIP1 from from COMMON_MAPPING where IP like VAR_IP ; 

       ELSE
         DBMS_OUTPUT.PUT_LINE('No Record found in table COMMON_MAPPING');

       END IF;

..............
..............
..............  

3 个答案:

答案 0 :(得分:1)

  

我在网上查了一下,发现我们可以通过DATANOTFOUND异常

来捕获它

您需要使用 NO_DATA_FOUND 例外。

例如,

SQL> SET serveroutput ON
SQL> DECLARE
  2    v_ename VARCHAR2(20);
  3    v_empno NUMBER;
  4  BEGIN
  5    v_empno := 9999;
  6    SELECT ename INTO v_ename FROM emp WHERE empno = v_empno;
  7    IF v_ename IS NOT NULL THEN
  8      dbms_output.put_line('Employee found');
  9      -- do something
 10    END IF;
 11  EXCEPTION
 12  WHEN no_data_found THEN
 13    dbms_output.put_line('Inside Exception because Employee not found');
 14    -- do something
 15  WHEN OTHERS THEN
 16    DBMS_OUTPUT.PUT_LINE ('Unexpected error');
 17    RAISE;
 18  END;
 19  /
Inside Exception because Employee not found

PL/SQL procedure successfully completed.

通常,您不在生产代码中使用 DBMS_OUTPUT ,使用异常块将错误记录到错误记录表中。它应该是健壮的,有助于开发人员轻松地理解错误以进行调试。

来自documentation

  

异常处理程序的优点

     

使用异常处理程序进行错误处理使程序更容易   写作和理解,并减少未处理的可能性   异常。

     

没有异常处理程序,您必须检查每个可能的错误,   它可能发生的任何地方,然后处理它。这很容易   忽略可能的错误或可能发生的地方,尤其是   如果错误不能立即检测到(例如,错误的数据   在计算中使用它之前可能无法检测到)。   错误处理代码分散在整个程序中。

     

使用异常处理程序,您无需知道每个可能的错误或   它可能发生在任何地方。你只需要包括一个   每个块中可能发生错误的异常处理部分。在里面   异常处理部分,您可以包含两者的异常处理程序   具体和未知错误。如果块中的任何位置发生错误   (包括在子块内),然后异常处理程序处理它。   错误处理代码在异常处理部分中被隔离   块。

关于您的效果问题:

  

并且可以使用外部光标。

我认为你的意思是显式光标。从性能的角度来看,这不是一个好主意。我希望结合使用 CURSOR FOR LOOP FOR ALL 语句以及 BULK COLLECT

在最近的Oracle版本中,通过内部bulk collect limit 100 更好地优化 CURSOR FOR LOOP

但是,它不只是关于批量收集,我们正在处理我们随后将对我们已逐步获取的数组执行的操作。我们可以通过使用FORALL语句和BULK COLLECT进一步提高性能。

话虽如此,如果你能在纯SQL 中做到这一点会快得多。 SQL和PL / SQL不同,当您在PL / SQL 中执行 SQL调用时,需要在两个引擎之间进行上下文切换,反之亦然,即 PL / SQL调用SQL 即可。随着两个引擎之间的每个上下文切换,都存在性能开销。当SQL调用PLSQL时,“命中”最明显 - 当SQL嵌入PLSQL时,其他方式则不然。

答案 1 :(得分:0)

你有3种可能性。

您的计数方法不安全(我的选项2)。 如果另一个会话在带有count的行之后和带有select ...的行之前删除满足条件的行 - 代码将抛出一个未处理的异常......

选项1是首选。但是,由于处理异常,性能可能会略低一些。但是,与没有处理异常的问题相比,它并不重要。 您提到了选项3 - 有一个示例如何使用它。

选项1:

select Out_IP,Out_IP1 INTO VAR_OUTIP, VAR_OUTIP1 from COMMON_MAPPING where IP like VAR_IP;
EXCEPTION
WHEN NO_DATA_FOUND
...

选项2:

 select count(*) into CNT2  from COMMON_MAPPING where IP like VAR_IP ; 

       IF CNT2 >0 THEN
         select Out_IP,Out_IP1 into VAR_OUTIP, VAR_OUTIP1 from from COMMON_MAPPING where IP like VAR_IP ;

选项3:

DECLARE
BEGIN
  FOR foo_rec IN ( select Out_IP,Out_IP1 from COMMON_MAPPING where IP like VAR_IP)
 LOOP
     ...
  END LOOP;
EXCEPTION
  WHEN OTHERS THEN
    RAISE;
END ;

答案 2 :(得分:0)

根据提供的问题陈述。我可以建议以下两种方法。     第一个选项是使用Ref Cursor,因为您不必担心这种异常处理。 - 我的最爱     第二种方法如上所述,在异常块中捕获异常。我在异常处理中包含了另一个OTHERS子句,因为可能会发生其他错误,例如TOO_MANY_ROWS。

-- Option 1  --> Using Ref Cursor
DECLARE
  CNT2 PLS_INTEGER:=0;
  oAuditMsg  VARCHAR2(100 CHAR);
  VAR_OUTIP  VARCHAR2(100 CHAR);-- Assuming this field is VARCHAR
  VAR_OUTIP1 VARCHAR2(100 CHAR);-- Assuming this field is VARCHAR
  VAR_IP     VARCHAR2(100 CHAR);
  p_lst sys_refcursor;
BEGIN
  oAuditMsg:= 'audit : ';
  DBMS_OUTPUT.PUT_LINE('Debug :: inside begin' );
  OPEN p_lst FOR SELECT Out_IP, Out_IP1 FROM COMMON_MAPPING WHERE IP LIKE VAR_IP ;
END;

-- Option 2  --> Using NoDataFound Exception Clause

DECLARE
--  CNT2 PLS_INTEGER:=0; -- Not required
  oAuditMsg  VARCHAR2(100 CHAR);
  VAR_OUTIP  VARCHAR2(100 CHAR);-- Assuming this field is VARCHAR
  VAR_OUTIP1 VARCHAR2(100 CHAR);-- Assuming this field is VARCHAR
  VAR_IP     VARCHAR2(100 CHAR);
--  p_lst sys_refcursor; -- Not required
BEGIN
  oAuditMsg:= 'audit : ';
  DBMS_OUTPUT.PUT_LINE('Debug :: inside begin' );
  BEGIN
  SELECT Out_IP, Out_IP1 
  INTO 
  VAR_OUTIP,
  VAR_OUTIP1
  FROM COMMON_MAPPING WHERE IP LIKE VAR_IP ;
  EXCEPTION WHEN NO_DATA_FOUND THEN
  RAISE_APPLICATION_ERROR(-20001,SQLERRM,TRUE);
  WHEN OTHERS THEN
  RAISE_APPLICATION_ERROR(-20001,SQLERRM,TRUE);
  END;
END;

如果有帮助,请告诉我。