3

所以我有一个 for 循环来处理一个 ID 列表并且有一些相当复杂的事情要做。不涉及所有丑陋的细节,基本上是这样的:

    宣布
      l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;

      ……剪……
    开始

      -- 获取列表ID
      l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
      -- 在一个很好的循环中处理每个
      FOR i IN 1..l_selected.count
      环形
        - 做一些数据检查的东西......

        -- 在这里我们将寻找重复的条目,所以如果发现重复我们可以 noop
        开始
          从 org_county_accountable 中选择county_id 到v_dup_check
          其中organization_id = :P4_ID AND County_id = v_county_id;
          -  下一个;!不;!但没有下一个!
        当 NO_DATA_FOUND THEN 时出现异常
          dbms_output.put_line('没有找到副本,继续');
        结尾;
        -- 这里有我们只想在没有欺骗的情况下执行的代码
        如果 v_dup_check 为空 那么
          -- 如果不是重复记录,请继续...

        别的
          -- 重置重复检查变量
          v_dup_check := NULL;
        结尾;
      结束循环;
    结尾;

我通常如何处理这个问题是通过选择一个值,然后将以下代码包装在 IF 语句中检查以确保重复检查变量为 NULL。但这很烦人。我只想说NEXT;或 NOOP;或者其他的东西。特别是因为我已经必须捕获 NO_DATA_FOUND 异常。我想我可以给甲骨文写一封信,但我很好奇其他人是如何处理这个问题的。

我也可以将它包装在一个函数中,但我正在寻找更清洁/更简单的东西。

4

8 回答 8

4

Oracle 11g 向 PL/SQL 中添加了 C 风格的“继续”循环结构,这在语法上听起来就像您正在寻找的那样。

出于您的目的,为什么不在进入循环之前消除重复项?这可以通过使用表函数查询 l_selected 来完成,然后过滤掉您不想要的记录,而不是遍历每个值。就像是...

declare

l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;

cursor no_dups_cur (p_selected APEX_APPLICATION_GLOBAL.VC_ARR2) is 
  select * from (
  select selected.*, 
         count(*) over (partition by county_id) cnt -- analytic to find counts grouped by county_id
    from table(p_selected) selected -- use table function to treat VC_ARR2 like a table 
    ) where cnt = 1 -- remove records that have duplicate county_ids
    ;

begin

l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);

for i in no_dups_cur(l_selected) loop

  null; -- do whatever to non-duplicates 

end loop;

end;

只需用您自己的逻辑替换确定“重复”的逻辑(您的示例中没有足够的信息来真正回答该部分)

于 2009-01-09T20:28:43.773 回答
3

与其捕获NO_DATA_FOUND,不如将匹配条目的数量选择到变量中,例如l_count,如果该计数为零,则继续进行?类似于以下内容:

    宣布
      l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
      l_count 整数;

      ……剪……
    开始

      -- 获取列表ID
      l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
      -- 在一个很好的循环中处理每个
      FOR i IN 1..l_selected.count
      环形
        - 做一些数据检查的东西......

        -- 这里我们将计算重复的条目,所以如果发现重复我们可以 noop
        从 org_county_accountable 中选择 COUNT(*) 到 l_count
         其中organization_id = :P4_ID AND County_id = v_county_id;
        如果 l_count = 0 那么
          -- 这里有我们只想在没有欺骗的情况下执行的代码
          -- 如果不是重复记录,请继续...

        万一;
      结束循环;
    结尾;
于 2009-01-09T20:28:21.357 回答
1

when_no_data_found exception也可以计算行数(请参阅 Pourquoi Litytestdata),但您也可以在块中执行您想要执行的操作。

declare 
  l_selected    apex_application_global.vc_arr2;
  l_county_id   org_county_accountable.count_id%type;
begin
  l_selected := apex_util.string_to_table(:p4_select_lst);
  for i in l_selected.first..l_selected.last loop
    begin
      select count_id
      into   l_county_id
      from   org_county_accountable
      where  organization_id = :p4_id
      and    county_id       = v_county_id;
    exception
      when no_data_found then 
        -- here we have code we only want to execute if there are no dupes already
        -- if not a duplicate record, proceed...
    end;
  end loop;
end;
于 2009-01-09T20:55:22.737 回答
1
<xmp>
<<next_loop>>
loop
...
...
if ....
then
   goto next_loop;

</xmp>
于 2011-07-20T18:03:28.723 回答
0

这是 GOTO 语句可能有用的情况。请参阅控制结构中的Oracle 文档以了解如何执行此操作。此外,您可能想在此处搜索以了解如何查询记录的存在。运行查询并等待异常并不是最优的。

于 2009-01-09T20:26:04.170 回答
0

另一种方法 - 将检查转换为本地函数:

DECLARE
  l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;

  ...snip...
  FUNCTION dup_exists 
     ( p_org_id org_county_accountable.organization_id%TYPE
     , p_county_id org_county_accountable.county_id%TYPE
     ) RETURN BOOLEAN 
  IS
    v_dup_check org_county_accountable.county_id%TYPE;
  BEGIN
    SELECT county_id INTO v_dup_check FROM org_county_accountable
    WHERE organization_id = p_org_id AND county_id = p_county_id;
    RETURN TRUE;
  EXCEPTION WHEN NO_DATA_FOUND THEN
    RETURN FALSE;
  END;
BEGIN

  -- get the list ids
  l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
  -- process each in a nice loop
  FOR i IN 1..l_selected.count
  LOOP
    -- do some data checking stuff...

    -- here we have code we only want to execute if there are no dupes already
    IF NOT dup_exists (:P4_ID, v_county_id) THEN
      -- if not a duplicate record, proceed...

    END;
  END LOOP;
END;

当然,如果您愿意,可以重写本地函数以使用 count 方法:

  FUNCTION dup_exists 
     ( p_org_id org_county_accountable.organization_id%TYPE
     , p_county_id org_county_accountable.county_id%TYPE
     ) RETURN BOOLEAN 
  IS
    l_count INTEGER;
  BEGIN
     SELECT COUNT(*) INTO l_count 
       FROM org_county_accountable
      WHERE organization_id = p_org_id AND county_id = p_county_id;
     RETURN (l_count > 0);
  END;
于 2009-01-14T17:31:18.480 回答
0

另一种方法是引发和处理用户定义的异常:

DECLARE
  l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
  duplicate_org_county EXCEPTION;

  ...snip...
BEGIN

  -- get the list ids
  l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
  -- process each in a nice loop
  FOR i IN 1..l_selected.count 
  LOOP
    BEGIN
      -- do some data checking stuff...

      -- here we will look for duplicate entries, so we can noop if duplicate is found
      BEGIN
        SELECT county_id INTO v_dup_check FROM org_county_accountable
        WHERE organization_id = :P4_ID AND county_id = v_county_id;
        RAISE duplicate_org_county;
      EXCEPTION WHEN NO_DATA_FOUND THEN
        dbms_output.put_line('no dups found, proceeding');
      END;
      -- here we have code we only want to execute if there are no dupes already

    EXCEPTION
      WHEN duplicate_org_county THEN NULL;
    END;
  END LOOP;
END;

我通常不会这样做,但如果有六个理由跳转到下一条记录,这可能比多个嵌套的 IF 更可取。

于 2009-01-14T17:53:33.770 回答
0

我知道这是一个老歌,但我不禁注意到上面的答案都没有考虑游标属性

有四个与游标相关的属性:ISOPEN、FOUND、NOTFOUND 和 ROWCOUNT。可以使用 % 分隔符访问这些属性,以获取有关游标状态的信息。

游标属性的语法是:

cursor_name%attribute

其中 cursor_name 是显式游标的名称。

因此,在这种情况下,您可以出于您的目的使用 ROWCOUNT (表示到目前为止获取的行数),如下所示:

declare 
   aux number(10) := 0;
   CURSOR cursor_name is select * from table where something;
begin
     select count(*) into aux from table where something;
     FOR row IN cursor_name LOOP
        IF(aux > cursor_name%ROWCOUNT) THEN 'do something is not over';
        ELSE 'do something else';
        END IF;
     END LOOP;
end;
于 2015-02-02T13:42:31.230 回答