3

我有几个场景:

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

  2. 如果记录存在给定条件的记录,则从表 1 读取数据,否则从表 2 中读取给定条件的数据

在 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 异常。

目前我正在用 Cursors 重新实现它,这样我就有了这样的东西:

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

我想从性能的角度找出哪一个更好 - 一个带有游标,或者一个将 Select 放入变量并具有三个 no_data_found Exception 块的那个。我不想使用我们目前拥有的两阶段查询过程。

4

6 回答 6

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;

我相信这将比您的其他解决方案执行得更好,因为它做了尽可能少的工作来达到预期的结果。

请参阅忽略 Oracle DUP_VAL_ON_INDEX 异常有多糟糕?我证明了使用异常比计数来查看是否有任何数据表现得更好。

于 2009-04-29T13:24:32.443 回答
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 引擎进行两次上下文切换,而第二个选项只进行一次上下文切换。

于 2009-04-29T11:15:11.660 回答
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...

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

对于您的第二种情况,我认为您的意思是两个表中可能都有数据,并且您希望使用表 1 中的数据(如果存在),否则使用表 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
于 2009-04-29T12:45:33.270 回答
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;
/
于 2010-01-27T17:14:28.303 回答
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”将表明存在确切的一行并将服务于我们的目的。

于 2011-03-10T07:45:40.807 回答
0

使用循环的“for row 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;
于 2009-04-29T12:00:48.820 回答