我正在编写一个存储过程,需要在其中进行大量调整.根据C#.NET编码的一般知识,异常会损害性能,我总是避免在PL/SQL中使用它们.我在这个存储过程中的条件主要围绕是否存在记录,我可以用两种方法之一:
SELECT COUNT(*) INTO var WHERE condition; IF var > 0 THEN SELECT NEEDED_FIELD INTO otherVar WHERE condition; ....
-要么-
SELECT NEEDED_FIELD INTO var WHERE condition; EXCEPTION WHEN NO_DATA_FOUND ....
第二种情况对我来说似乎更优雅,因为那时我可以使用NEEDED_FIELD,我必须在第一种情况下的条件之后的第一个语句中选择.更少的代码.但是如果存储过程使用COUNT(*)运行得更快,那么我不介意再输入一点来弥补处理速度.
任何提示?我错过了另一种可能吗?
编辑 我应该提到这已经嵌套在FOR LOOP中了.不确定这是否与使用游标有所不同,因为我认为我不能将光标作为FOR LOOP中的选择进行DECLARE.
我不会使用显式游标来执行此操作.当可以使用隐式游标时,Steve F.不再建议人们使用显式游标.
该方法count(*)
不安全.如果另一个会话删除符合条件的行后面的行count(*)
,并且在行之前select ... into
,代码将抛出一个无法处理的异常.
原始帖子的第二个版本没有这个问题,通常是首选.
也就是说,使用异常会有一个小的开销,如果你100%确定数据不会改变,你可以使用count(*)
,但我建议不要这样做.
我在32位Windows上运行Oracle 10.2.0.1上的这些基准测试.我只关注经过的时间.还有其他测试工具可以提供更多细节(例如锁存器计数和使用的内存).
SQL>create table t (NEEDED_FIELD number, COND number);
表创建.
SQL>insert into t (NEEDED_FIELD, cond) values (1, 0);
已创建1行.
declare otherVar number; cnt number; begin for i in 1 .. 50000 loop select count(*) into cnt from t where cond = 1; if (cnt = 1) then select NEEDED_FIELD INTO otherVar from t where cond = 1; else otherVar := 0; end if; end loop; end; /
PL/SQL过程成功完成.
经过时间:00:00:02.70
declare otherVar number; begin for i in 1 .. 50000 loop begin select NEEDED_FIELD INTO otherVar from t where cond = 1; exception when no_data_found then otherVar := 0; end; end loop; end; /
PL/SQL过程成功完成.
经过时间:00:00:03.06
由于SELECT INTO假定将返回单个行,因此您可以使用以下形式的语句:
SELECT MAX(column) INTO var FROM table WHERE conditions; IF var IS NOT NULL THEN ...
SELECT将为您提供值(如果有),值为NULL而不是NO_DATA_FOUND异常.由于结果集包含单行,MAX()引入的开销将为最小为零.它还具有相对于基于游标的解决方案而言紧凑的优点,并且不易受原始帖子中的两步解决方案等并发问题的影响.
@ Steve的代码的替代品.
DECLARE CURSOR foo_cur IS SELECT NEEDED_FIELD WHERE condition ; BEGIN FOR foo_rec IN foo_cur LOOP ... END LOOP; EXCEPTION WHEN OTHERS THEN RAISE; END ;
如果没有数据,则不执行循环.光标FOR循环是要走的路 - 它们有助于避免大量的内务管理.更紧凑的解决方案:
DECLARE BEGIN FOR foo_rec IN (SELECT NEEDED_FIELD WHERE condition) LOOP ... END LOOP; EXCEPTION WHEN OTHERS THEN RAISE; END ;
如果您在编译时知道完整的select语句,那么哪个有用.