【问题标题】:Implicit cursor and NO_DATA_FOUND exception隐式游标和 NO_DATA_FOUND 异常
【发布时间】:2017-02-28 11:36:02
【问题描述】:

我有以下PL/SQL 代码:

BEGIN
  FOR c IN (SELECT ...) LOOP
     <code1>;
  END LOOP;
  <code2>;
EXCEPTION WHEN NO_DATA_FOUND THEN
  NULL;
END;

此代码应在一个循环内多次运行code1,并在完成此循环后执行code2。否则,如果 SELECT 查询未找到数据,那么我预计这会引发异常并超出 code2,但这不会发生。为什么?

【问题讨论】:

  • 不,这不是应该发生的!如果没有数据,则循环运行 0 次 - 即它跳过并执行 code2
  • 好吧,就是这样,虽然我预计 SELECT 语句中的 0 条记录会引发异常,那么如果没有记录,我该如何解决我的问题以避免执行 code2?

标签: sql oracle exception-handling


【解决方案1】:

NO_DATA_FOUND 是由必须只返回一行但找不到匹配行的语句抛出的,例如

DECLARE x NUMBER;
BEGIN
  SELECT foo INTO x FROM bar WHERE xyz='abc';
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    ...
END;

在您的情况下,您可以执行以下操作:

DECLARE foundSomething BOOLEAN := FALSE;
BEGIN
  FOR c IN (SELECT ...) LOOP
     foundSomething := TRUE;
     <code1>;
  END LOOP;     
  IF NOT foundSomething THEN
    NULL;  -- handle the situation
  ELSE
    <code2>;
  END IF;
END; 

【讨论】:

  • 好的,这是带有布尔标志的经典方法(如果进入循环),但我希望在 PL/SQL 中有其他东西,比如 IF sql%rowcount 或者当 SELECT 返回不止一个时行。
  • 另外,如何在不使用 LOOP 内的显式迭代器的情况下查找 SELECT 获取的记录数?
  • 不幸的是,没有直接的方法来获取循环游标中获取的记录数 - sql%rowcount 在这种情况下不起作用,您肯定已经发现了。
  • 只是想知道,您说 NO_DATA_FOUND 是由必须恰好返回一行的语句抛出的。 Oracle 如何知道该语句是否会返回一行或多行(好的,如果搜索字段是 PK 或 UQ 列),但从您上面的 SELECT 语句(SELECT foo INTO x FROM bar WHERE xyz='abc')无法提前知道.
  • @sbrbot:由于 INTO 子句,这些语句 (SELECT ... INTO ... ) 必须返回一行。您希望在x 中存储一个结果。如果没有这样的行,NO_DATA_FOUND 将被抛出。如果多于一排,TOO_MANY_ROWS 将被抛出。 IE。在这种情况下,必须意味着您,开发人员,必须小心地设计语句,使其只返回一个结果。
【解决方案2】:

不,这不是应该发生的。

如果没有数据,则循环运行 0 次 - 即跳过 code1 并执行 code2。

您可以像这样定义显式游标并检查数据是否不可用:

DECLARE
  cursor cur is select 1 a from dual where 1 = 1;
  type tab is table of cur%rowtype;
  v tab;
BEGIN
  open cur;
  loop
    fetch cur bulk collect into v;
    if v.count = 0 then
        raise no_data_found;
    end if;
    dbms_output.put_line('Code1');
  end loop;
  close cur;
  dbms_output.put_line('Code2');
EXCEPTION 
  WHEN NO_DATA_FOUND THEN
    dbms_output.put_line('Error');
END;
/

您可以轻松扩展此代码以执行其他操作,例如将提取分成批次等。

【讨论】:

  • 您将我的隐式光标转换为显式光标,然后检查了 v.COUNT 属性(光标中的前数)。隐式游标可以实现这样的事情吗?
  • @sbrbot - 您可以使用标志变量在循环内设置,在循环后检查并在需要时引发异常,如其他答案所示。 AFAIK 没有内置机制。
猜你喜欢
  • 2011-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多