Oracle 新手,不确定我是不是“遗漏”了啥

Posted

技术标签:

【中文标题】Oracle 新手,不确定我是不是“遗漏”了啥【英文标题】:New to Oracle, Unsure if I'm "missing" somethingOracle 新手,不确定我是否“遗漏”了什么 【发布时间】:2014-10-21 17:39:22 【问题描述】:

以下通过 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 事物”,否则我看不出有什么好的理由说明为什么这个逻辑会失败。

我确实认识到我更多地使用通用脚本语言方法来执行我的逻辑,而不是嵌套的 Oracle 语句,但我看不出有什么理由会有所作为。

同样,这里可能是一些无知。

提前谢谢你。

【问题讨论】:

【参考方案1】:

问题在于rownum 的工作方式。只能使用rownum &lt;rownum &lt;=,或rownum = 1;您不能直接与 1 以外的任何值进行比较,也不能使用 &gt;&gt;= 运算符。

来自Tom Kyte's article:

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

select * 
  from t 
 where ROWNUM > 1;

所以您使用rownum &lt;= 20 的主要查询很好,并且在循环的第一次迭代中where rownum = 1 也可以。但是在第二次迭代中,rownum = 2 永远不可能是真的,所以你会得到一个 no-data-found 错误。

当你有一个结果集时使用你自己的变量作为循环迭代器做的工作比你需要的要多。您可以通过在子查询中包含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. 前缀(使用更有意义的名称!)和游标查询中的列名/别名来引用它们。

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

【讨论】:

有趣,好吧,所以在这种情况下,我需要确定一种方法来迭代每一行的自定义值......也许将选择的输出转储到一个 temp_table 中,我可以在其中定义一个 rowID 为例如? @GabrialMacLeod - 您可以选择一个集合并对其进行迭代,或者使用一个临时表,但这在 Oracle 中几乎总是矫枉过正(尽管我相信在 SQL Server 中很常见)。我已经使用您的原始查询添加了一个使用游标循环的版本,我认为这将满足您的需求。【参考方案2】:

很可能结果集中没有足够的记录来完成循环。例如,如果您只有两条记录,则询问 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;

更改“NULL”以处理异常,但这应该只是为您退出循环。

【讨论】:

如下:select count(*) into members from member wherebirth_date > 0 and update_end_date = 29991231; dbms_output.put_line('成员记录:' || members);返回超过 400,000 条记录,因此我高度怀疑返回记录不足的问题,因为我将迭代限制为 10 个循环(行)

以上是关于Oracle 新手,不确定我是不是“遗漏”了啥的主要内容,如果未能解决你的问题,请参考以下文章

关于在 Xcode 上导入 Alamofire,我是不是遗漏了啥?

我是不是遗漏了啥或者这是创建自定义控件/组件的方式

SQL主键分配错误..不确定我做错了啥? [关闭]

Azure 启动运行手册停止工作,不确定发生了啥?

Httpservlet错误404,不确定我做错了啥我已经尝试了所有解决方案

无法使用 Travis-CI 运行可执行文件-不确定我做错了啥