因为以前看到有的同事sql脚本中的一些写法,本意是使用oracle的continue用法,结果却事与愿违。我正好周末无事时写了一个脚本,用到这个,现分享给大家。
我们知道,像C、JAVA、C#等编程语言中都有continue,连python这种解释性语言都有continue,那oracle有没有continue呢?具体说,是有的,但要在oracle 11g之后才有。那对于10g跟之前的版本要如何实现continue的用法呢?看下面的脚本,只选了部分的内容:
  for c in get_info(v_seg) loop
    if (c.last_analyzed is null or
      round(sysdate - c.last_analyzed) > v_days) then
      begin
        dbms_stats.gather_table_stats(ownname          => c.owner,
                                      tabname          => c.table_name,
                                      partname        => null,
                                      estimate_percent => 0.0001);
      exception
        when others then
          null;
      end;
    end if;
  end loop;
这段sql主要用途是对一个cursor中的所有表进行统计信息收集,其实完全可以使用一个这样的循环就可以搞定的:
  for c in get_info(v_seg) loop
    if (c.last_analyzed is null or
      round(sysdate - c.last_analyzed) > v_days) then
        dbms_stats.gather_table_stats(ownname          => c.owner,
                                      tabname          => c.table_name,
                                      partname        => null,
                                      estimate_percent => 0.0001);
    end if;
  end loop;
但是,由于统计信息收集时,有时会遇到权限不足(按我们的库经常会遇到)的问题,如果报错,将跳出此循环,其他的表将得不到统计信息收集。这时需要我们使用continue,来跳过此循环,去分析下一个表。但此库正好是一个10g的库,不能使用11g的continue关键字,更何况我们写脚本,一定要做到的一点是,通用,即在常用的oracle版本(目前一般是指8i-11g,当然我们就是10g-11g)中都能使用。于是我们使用了嵌套循环的办法,即
加入一个新的pl/sql语句块,并加入异常处理,即让异常的捕获是在循环之内,还不是循环之外的异常去捕获到它。不过这里就比较简单的一个null了,当然可以使用一些执行纪录等。当业务较为复杂时,可以建立自定义的异常来捕获:
declare
  pi constant number(9, 7) := 3.1415927;
  radius integer(5);
  area  number(14, 2);
  e_userdefinedexception exception;
  pragma exception_init(e_userdefinedexception, -1401);
begin
  radius := 0;
  loop
    radius := radius + 1;
    begin
      if radius = 4 then
        raise e_userdefinedexception;
      else
        area := pi * power(radius, 2);
        insert into areas values (radius, area);
        exit when area > 100;
      end if;
    exception
      when e_userdefinedexception then
        nullisnull的用法;
    end;
  end loop;
  commit;
end;
还有一种办法是在pl/sql的声明部分使用procedure,不过原理上面是一样的。我们再说下可以使用goto的方法:
declare
  pi  constant number (9, 7)  := 3.1415927;
  radius        integer (5);
  area          number (14, 2);
begin
  radius := 0;
  loop
      <<here>>
      radius := radius + 1;
      if radius = 4
      then
        goto here;
      else
        area := pi * power (radius, 2);
        insert into areas
              values (radius, area);
        exit when area > 100;
      end if;
  end loop;
  commit;
end;
貌似goto看起来更简洁,不过我们知道C语言的灵魂是指针,但这也是goto的原理,也是被诟病的地方,即goto破坏程序的可读性跟可维护性。Pl/sql也是是高级编程语言,同样也不推荐使用goto的方法,因为非常容易出错,导致业务逻辑出现问题。应优先使用第一种方法。另外,我们说到11g有了正统的continue,我们看下它的用法,其实跟普通编程语言中的用法完全一样:
declare
  total int := 0;
begin
  for i in 1 .. 10 loop
    if mod(i, 2) = 0 then
      continue;
    end if;
    total := total + i;
  end loop;
  dbms_output.put_line(total);
end;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。