chao's profile清凉水都PhotosBlogListsMore Tools Help

Blog


    March 21

    PL/SQL FORALL语句的一些注意事项

    Technorati 标签: ,

    1.对于引用的处理
    FORALL在使用集合的时候无法用引用的方式调用数据,例如:

    Forall i in tab.First .. tab.Last
       delete  Table where col = tab(i).col

    这样是无法编译通过的,所以不要指望用Selete * Bulk Collecte into row_tab到一个记录集合,在通过FORALL的方式来快速处理表格数据了,要做的话只能用标准SQL语句解决了,或者一列一列的改(注:多集合的情况下,FORALL会默认按FOR……LOOP的方式处理数据,就谈不上优化了)。

    2.关于异常处理的一些例外和机制猜想
    虽然Forall可以通过调用Save Exceptions和块的Exception来跳过错误继续执行后面DML,但是当因为调用的集合缺失数据而发生错误的时候并不一定会跳过继续进行,下面举个简单的例子:

    Declare
    TYPE t_tab IS TABLE OF Varchar2(3);

    v_tab  t_tab := t_tab('1','2','3','4','5','6');

    BEGIN
    v_tab.DELETE(1);
    v_tab.DELETE(2);
    v_tab.DELETE(5);

    FORALL i IN v_tab.FIRST .. v_tab.LAST SAVE EXCEPTIONS
    UPDATE  TestTable  SET t_value = 'Changed'
    WHERE   t_id = v_tab(i);

    DBMS_OUTPUT.PUT_LINE('END');

    EXCEPTIONS
    WHEN others THEN
    NULL;
    END;

    最终结果是: 1: Unchanged
      2: Unchanged
      3: Changed
      4: Changed
      5: Unchanged
      6: Unchanged

     

    这个结果是不是看起来很奇怪,如果按照无SAVE EXCEPTIONS的情况下去想,那么结果都应该为Unchanged,如果按照有SAVE EXCEPTIONS的一般情况来想则6应当为Changed。

    根据FORALL的处理流程和上面的结果,我个人认为FORALL在执行的时候是这样的:

    1.生成所有集合记录执行的DML语句
    2.将所有语句一起发送给执行器执行
    3.执行器逐条执行语句,和For……Loop的方式相似,自动提交和回滚每条记录

    因为只提交一次DML所以在集合中有多条集合时,效率会大大优于For……Loop。那么产生上述问题的原因就在第一步生成DML语句的时候了。由于1和2的数据不存在,无法产生DML语句,于是继续检索后面的集合,到3的时候可以正常生成。到5的时候又不能生成了,关键点就在这之后的处理了,引擎并没有尝试去生成后面的语句,而是直接跳出的生成环节,所以6理所当然的没有被更新了,因为根本没有生成对应的语句。

    因此对于添加SAVE EXCEPTIONS的FORALL的错误处理,是分为两个部分的:

    1.在生成语句阶段,引擎是从生成第一个正常语句开始到再遇到错误截止,换句话说只能跳过头部的连续错误,而中间的错误会影响其后面的操作。
    2.在执行阶段,各个语句相对独立,任何错误都不影响其他语句的执行。

    最后,建议各位在使用FORALL的时候最好用变长数组(VARRAY)不要用 嵌套表(TABLE),嵌套表是不连续的,而且变长数组(VARRAY)可以避免删除操作,如果实在需要用嵌套表(TABLE)的话,一定要记得检查是否有空数据,然后用TRIM彻底删除后,再用FORALL执行,以避免发生上述问题。