在ITPUB上看到一个FORALL执行UPDATE的帖子,觉得有点意思,简单记录一下。




以前研究过FORALL中的INSERT语句,发现语句通过绑定数组的方式,实现了批量绑定,一次运行的方式,从而提高了执行的效率。


但是对于UPDATE语句而言,Oracle的实现和INSERT是不同的:


SQL> CREATE TABLE T AS SELECT ROWNUM ID FROM TAB;


表已创建。


SQL> SELECT * FROM T;


      ID


----------


       1


       2


       3


       4


       5


       6


已选择6行。


SQL> DECLARE


2  TYPE T_ID IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;


3  V_ID T_ID;


4 BEGIN


5  SELECT ID


6  BULK COLLECT INTO V_ID


7  FROM T;


8  FORALL I IN V_ID.FIRST..V_ID.LAST


9   UPDATE T


10   SET ID = V_ID(I);


11 END;


12 /


PL/SQL过程已成功完成。


SQL> SELECT * FROM T;


      ID


----------


       6


       6


       6


       6


       6


       6


已选择6行。


SQL> ROLLBACK;


回退已完成。


结果并不像想象中的,仍然是123456,而是所有的记录都被更新为6。显然Oracle不太可能只使用数组变量在中最后一个值进行更新,因此T表中的记录应该是被更新了6次,所以对于UPDATE语句而言,绑定数组中有多少个值,更新就运行了多少次,而对于当前没有指定WHERE条件,所以每次运行的时候所有的记录都被更新。


正确的写法类似于:


SQL> DECLARE


2  TYPE T_ID IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;


3  V_ID T_ID;


4 BEGIN


5  SELECT ID


6  BULK COLLECT INTO V_ID


7  FROM T;


8  FORALL I IN V_ID.FIRST..V_ID.LAST


9   UPDATE T


10   SET ID = V_ID(I)


11   WHERE ID = V_ID(I);


12 END;


13 /


PL/SQL过程已成功完成。


SQL> SELECT * FROM T;


      ID


----------


       1


       2


       3


       4


       5


       6


已选择6行。


下面验证一下刚才的分析是否准确:


SQL> CREATE OR REPLACE TRIGGER T_STATEMENT


2 BEFORE UPDATE ON T


3 BEGIN


4  DBMS_OUTPUT.PUT_LINE('STATEMENT');


5 END;


6 /


触发器已创建


SQL> CREATE OR REPLACE TRIGGER T_ROW


2 BEFORE UPDATE ON T


3 FOR EACH ROW


4 BEGIN


5  DBMS_OUTPUT.PUT_LINE('ROW:' || :OLD.ID);


6 END;


7 /


触发器已创建


SQL> SET SERVEROUT ON


SQL> DECLARE


2  TYPE T_ID IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;


3  V_ID T_ID;


4 BEGIN


5  SELECT ID


6  BULK COLLECT INTO V_ID


7  FROM T;


8  FORALL I IN V_ID.FIRST..V_ID.LAST


9   UPDATE T


10   SET ID = V_ID(I)


11   WHERE ID = V_ID(I);


12 END;


13 /


STATEMENT


ROW:1


STATEMENT


ROW:2


STATEMENT


ROW:3


STATEMENT


ROW:4


STATEMENT


ROW:5


STATEMENT


ROW:6


PL/SQL过程已成功完成。


SQL> DECLARE


2  TYPE T_ID IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;


3  V_ID T_ID;


4 BEGIN


5  SELECT ID


6  BULK COLLECT INTO V_ID


7  FROM T;


8  FORALL I IN V_ID.FIRST..V_ID.LAST


9   UPDATE T


10   SET ID = V_ID(I);


11 END;


12 /


STATEMENT


ROW:1


ROW:2


ROW:3


ROW:4


ROW:5


ROW:6


STATEMENT


ROW:1


ROW:1


ROW:1


ROW:1


ROW:1


ROW:1


STATEMENT


ROW:2


ROW:2


ROW:2


ROW:2


ROW:2


ROW:2


STATEMENT


ROW:3


ROW:3


ROW:3


ROW:3


ROW:3


ROW:3


STATEMENT


ROW:4


ROW:4


ROW:4


ROW:4


ROW:4


ROW:4


STATEMENT


ROW:5


ROW:5


ROW:5


ROW:5


ROW:5


ROW:5


PL/SQL过程已成功完成。


对于FORALL中的UPDATE语句,执行的次数由FORALL语句中数组的长度决定,这与INSERT语句只运行一次是有明显不同的。



oracle视频教程请关注:http://u.youku.com/user_video/id_UMzAzMjkxMjE2.html