Oracle Ref Cursor Vs选择进入异常处理

时间:2009-04-29 10:47:12

标签: oracle plsql cursor

我有几个场景:

  1. 需要以预定义的顺序从三个不同的表中读取列的值,并且只有一个表具有数据

  2. 如果对于给定的标准存在记录,则从table1读取数据否则从表2中读取给定标准的数据

  3. 在Oracle存储过程中

    现在处理这些问题的方法是先将给定查询的计数输入变量,如果计数>> 0,然后我们执行相同的查询来读取实际数据,如:

    select count(*) from table1 into v_count
    if v_count > 0
    then
        select data into v_data from table1
    end if;
    
    Return v_data
    

    这样做是为了避免no_data_found异常,否则我需要三个异常处理程序块来捕获每个表访问的no_data_found异常。

    目前我正在用游标重新实现这一点,以便我有这样的东西:

    cursor C1 is
        select data from table1;
    Open C1
    Fetch C1 into v_data
    if C1%FOUND
    then
        Close C1
        Return v_data
    End If
    

    我想从性能的角度找出哪一个更好 - 一个是Cursors,另一个是选择变量并且有三个no_data_found异常块。我不想使用我们目前的两阶段查询过程。

6 个答案:

答案 0 :(得分:5)

我不知道为什么你如此热衷于避免异常?有什么问题:

begin
    begin
        select data into v_data from table1;
    exception
        when no_data_found then
        begin
            select data into v_data from table2;
        exception
            when no_data_found then
            begin
               select data into v_data from table3;
            exception
                when no_data_found then
                    v_data := null;
            end;
        end;
    end;
    return v_data;
end;

我相信这会比你的其他解决方案表现得更好,因为它尽可能少地完成所需的工作。

请参阅How bad is ignoring Oracle DUP_VAL_ON_INDEX exception?,其中我证明使用异常的效果优于计数以查看是否有任何数据。

答案 1 :(得分:4)

select count(*) from table1 into v_count
if v_count > 0 then
    select data into v_data from table1;
else
    v_data := null;
end if;
return v_data;

不等同于

begin
    select data into v_data from table1;
    return v_data;
exception
    when no_data_found then
        return null;
end;

在多用户环境中。在第一种情况下,有人可以在您检查存在的点和读取数据之间更新表。

性能方面,我不知道哪个更好,但我知道第一个选项使两个上下文切换到sql引擎,第二个选项只进行一个上下文切换。

答案 2 :(得分:1)

现在处理方案1的方式并不好。当一个人满足要求时,你不仅要做两个查询,而且正如Erik指出的那样,它开启了两个查询之间数据变化的可能性(除非你使用只读或可序列化的事务)。

鉴于你说在这种情况下数据将只是三个表中的一个,那么这个怎么样?

SELECT data
  INTO v_data FROM
  (SELECT data FROM table1
   UNION ALL
   SELECT data FROM table2
   UNION ALL
   SELECT data FROM table3
  )

您可以使用另一个“技巧”来避免编写多个无数据的处理程序:

SELECT MIN(data) INTO v_data FROM table1;
IF v_data IS NOT NULL THEN
   return v_data;
END IF;

SELECT MIN(data) INTO v_data FROM table2;
...etc...

但我没有看到任何理由比拥有三个异常处理程序更好。

对于你的第二个场景,我认为你的意思是两个表中都可能有数据,你想使用table1中的数据(如果存在),否则使用表2中的数据。再次,你可以在一个表中执行此操作查询:

SELECT data
  INTO v_data FROM
  (SELECT data FROM
    (SELECT 1 sort_key, data FROM table1
     UNION ALL
     SELECT 2 sort_key, data FROM table2
    )
   ORDER BY sort_key ASC
  )
  WHERE ROWNUM = 1

答案 3 :(得分:1)

DECLARE
    A VARCHAR(35);
    B VARCHAR(35);
BEGIN
    WITH t AS
    (SELECT OM_MARCA, MAGAZIA FROM ifsapp.AKER_EFECTE_STOC WHERE (BARCODE = 1000000491009))
    SELECT
    (SELECT OM_MARCA FROM t) OM_MARCA,
    (SELECT MAGAZIA FROM t) MAGAZIA
    INTO A, B
    FROM DUAL;
    IF A IS NULL THEN
       dbms_output.put_line('A este null');
    END IF;
    dbms_output.put_line(A);
    dbms_output.put_line(B);
END;
/

答案 4 :(得分:1)

“Dave Costa”的MIN选项的增强版......

SELECT COUNT(1), MIN(data) INTO v_rowcount, v_data FROM table2;

现在v_rowcount可以检查值0,> 1(大于1),其中普通选择查询将抛出NO_DATA_FOUNDTOO_MANY_ROWS异常。值“1”表示确实存在一行并且将用于我们的目的。

答案 5 :(得分:0)

使用循环中的“for cursor in cursor”形式,如果没有数据,循环将不会处理:

declare cursor
t1Cur is
 select ... from table1;
t2Cur is
 select ... from table2;
t3Cur is
 select ... from table3;
t1Flag boolean FALSE;
t2Flag boolean FALSE;
t3Flag boolean FALSE;
begin
for t1Row in t1Cur loop
  ... processing, set t1Flag = TRUE
end loop;
for t2Row in t2Cur loop
  ... processing, set t2Flag = TRUE
end loop;
for t3Row in t3Cur loop
  ... processing, set t3Flag = TRUE
end loop;
... conditional processing based on flags
end;