Oracle的新手,不确定我是否会错过"某物

时间:2014-10-21 17:39:22

标签: oracle loops plsql while-loop

以下通过PL / SQL的查询返回预期结果:

select member_id, birth_date from ( select * from member order by member_id asc ) where birth_date > 0 and update_end_date = 29991231 and rownum <= 20;

它返回20行数据,按我想要的顺序排序。

然而,当我在SQL块中的while循环中包含该查询的变体时,它在循环的第二次迭代中出错:

set serveroutput on;

declare
    dob         number (8);
    currentDate number (8);
    mID         number (8);
    members     integer;
    i           integer;

begin
  select to_char(sysdate, 'YYYYMMDD') into currentDate from dual;
  dbms_output.put_line('Current Date: ' || currentDate);

  select count(*) into members from member where birth_date > 0 and update_end_date = 29991231;
  dbms_output.put_line('Member Records: ' || members);

  i := 1;
  while i <= 10
  loop 

    select member_id, birth_date into mID, dob from ( select * from member order by member_id asc ) where birth_date > 0 and update_end_date = 29991231 and rownum = i;

    dbms_output.put_line('Row number: ' || i || ' > Member ID: ' || mID || ' | Member DOB: ' || dob);

    i := i + 1;
  end loop;
end;
/

我们的想法是从有序结果的第一行开始,然后遍历每一行,输出该行的结果。

最终,一旦我完成了这项工作,我打算将这些数据与预期值进行比较,然后再执行更多的条件逻辑。

因此,除非我错过了一个基本的“Oracle Thing”,否则我认为没有任何充分的理由说明为什么这个逻辑会失败。

我确实认识到我使用更多的通用脚本语言方法来执行我的逻辑而不是嵌套的Oracle语句,但我没有看到为什么它应该有所作为的原因。

再次,在这里可能会有些无知。

提前谢谢。

2 个答案:

答案 0 :(得分:3)

问题在于rownum的工作原理。您只能使用rownum <rownum <=rownum = 1;您无法直接与1之外的任何内容进行比较,也无法使用>>=运算符。

来自Tom Kyte's article

  

RumnUM值在传递谓词后分配给一行   查询的阶段但在查询之前进行任何排序或   聚合。此外,ROWNUM值仅在其后递增   已分配,这就是为什么以下查询永远不会返回一行:

select * 
  from t 
 where ROWNUM > 1;

因此,使用rownum <= 20的主查询很好,并且在循环where rownum = 1的第一次迭代中也可以。但是在第二次迭代rownum = 2实际上永远不会是真的,所以你得到一个没有数据的错误。

当你有一个结果集时,使用你自己的变量作为循环迭代器正在做比你需要的更多的工作。您可以通过在子查询中包含rownum,然后在外部查询中对其进行过滤来使其工作,但它不会非常漂亮或高效。我认为,你可以从以下方面得到你需要的东西:

declare
    currentDate number (8);
begin
  select to_char(sysdate, 'YYYYMMDD') into currentDate from dual;
  dbms_output.put_line('Current Date: ' || currentDate);

  for r in (
    select member_id as mID, birth_date as dob, rownum as rn
    from (
      select * from member order by member_id asc
    )
    where birth_date > 0
    and update_end_date = 29991231
    and rownum <= 20
  )
  loop
    dbms_output.put_line('Row number: ' || r.rn
      || ' > Member ID: ' || r.mID
      || ' | Member DOB: ' || r.dob);
  end loop;
end;
/

这只在光标中执行一次查询,而不是为i的每个值部分重复一次。 Read more about cursors

我已经包含了rownum(带有别名,以便您稍后可以参考),其价值与您的i相同。您可以使用循环中的选定值执行任何操作,使用r.前缀(使用更有意义的名称!)和游标查询中的列名称/别名来引用它们。

将数据(或字符串)存储或操作日期不是一个好主意,如果你刚刚开始,这是一个坏习惯。始终使用正确的数据类型。

答案 1 :(得分:0)

很可能结果集中没有足够的记录来完成循环。例如,如果您只有两条记录,则要求记录rownum = 5会给您一个NO_DATA_FOUND异常。您可以在异常块中捕获它:

BEGIN
i := 1;
  while i <= 10
  loop 

    select member_id, birth_date into mID, dob from ( select * from member order by member_id asc ) where birth_date > 0 and update_end_date = 29991231 and rownum = i;

    dbms_output.put_line('Row number: ' || i || ' > Member ID: ' || mID || ' | Member DOB: ' || dob);

    i := i + 1;
  end loop;
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;

更改&#39; NULL&#39;处理异常,但这应该只是退出循环。