Oracle中FORALL执行UPDATE语句

在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

返回栏目页:http://www.bianceng.cnhttp://www.bianceng.cn/database/Oracle/

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语句只运行一次是有明显不同的。

作者:51cto博客 Oracle小混子

时间: 2024-08-24 16:50:32

Oracle中FORALL执行UPDATE语句的相关文章

大量数据执行-oracle在存储过程中执行update语句

问题描述 oracle在存储过程中执行update语句 在存储过程中经过加工对表执行update语句,但表的数据太大了,2百多万数据的表我就要执行1个多小时,,,8千多万数据的表要怎么办啊? 解决方案 有这样的思路你可参考下: 思路1.写存储过程,引入loop循环,分批次更新.提交: 思路2.以nologging形式重新建表,按新规则把数据插过来:

查询Oracle中正在执行和执行过的SQL语句_oracle

查询Oracle正在执行的sql语句及执行该语句的用户 SELECT b.sid oracleID, b.username 登录Oracle用户名, b.serial#, spid 操作系统ID, paddr, sql_text 正在执行的SQL, b.machine 计算机名 FROM v$process a, v$session b, v$sqlarea c WHERE a.addr = b.paddr AND b.sql_hash_value = c.hash_value 其它网友给出的正

Oracle中获取执行计划的几种方法分析

以下是对Oracle中获取执行计划的几种方法进行了详细的分析介绍,需要的朋友可以参考下   1. 预估执行计划 - Explain PlanExplain plan以SQL语句作为输入,得到这条SQL语句的执行计划,并将执行计划输出存储到计划表中. 首先,在你要执行的SQL语句前加explain plan for,此时将生成的执行计划存储到计划表中,语句如下: explain plan for SQL语句然后,在计划表中查询刚刚生成的执行计划,语句如下: select * from table(

sqlite-【Android】SQLite执行Update语句没结果

问题描述 [Android]SQLite执行Update语句没结果 数据库存在,且有数据,但是执行Update语句后没结果. ContentValues value = new ContentValues(); value.put(key,value); String[] args = {args}; DB.update(table_name ,value, "phone=?", args); 执行后没反应 解决方案 Android SQLite增删改查的语句android SQLit

hql-jdbc执行update语句,突然卡死了

问题描述 jdbc执行update语句,突然卡死了 我原先用的是hibernate的hql语句执行批量的update,并且是更新的同一条数据,执行一半的更新时,突然就卡住不动了,也不知道是什么原因 数据库是SqlServer2008.后来换成jdbc的sql语句执行批量更新(还是不断更新同一条记录),结果还是出现相同情况,程序卡住了(在执行jdbc.query()方法卡住了),我在用hibernate时,乐观锁和悲观锁都试过了,还是不行,jdbc也是,试过了给sql语句加锁,结果还是不行,请问各

c#-C#中数据库删除按钮中在下面update语句中显示数据库查询过于复杂

问题描述 C#中数据库删除按钮中在下面update语句中显示数据库查询过于复杂 string connstr = "Provider=Microsoft.Jet.OleDb.4.0;"; connstr += @"Data Source=GEARCNC.mdb"; OleDbConnection tempconn = null; try { if (MessageBox.Show("确定删除这一项吗?", "confirm Messag

如何清除oracle中的执行sql记录在日志里面的的记录

问题描述 如何清除oracle中的执行sql记录在日志里面的的记录 如何清除oracle中的执行sql记录在日志里面的的斤斤计较绝对经典如何清除oracle中的执行sql记录在日志里面的的记录 解决方案 http://www.zhihu.com/question/27579044

mysql 单引号 双引号-mysql 中,执行insert语句,单引号双引号区别

问题描述 mysql 中,执行insert语句,单引号双引号区别 建表的时候或者用Insert向表中插入记录时,会用到单引号和双引号.有时候感觉二者都行,但请问下这两者有什么区别.什么时候用单,什么时候用双?

Oracle中存储过程执行大的Sql语句时如何查看sql是否正确

  在Oracle中编写一个存储过程中,有时会用到较大的sql语句,但是通过PL/sql工具Test查看时又不能看到sql的样子,通过查看发现可以通过Oracle的物理临时表来处理, 在存储过程中添加DBMS_OUTPUT.put_line('要输出的Sql语句'); 然后在PL/sql工具中选中存储过程,右键选择Test执行,执行完成后可以通过DBMS_OUTPUT窗口查看sql结果了,很实用的