Connected to Oracle Database 10g Release 10.2.0.1.0
Connected as scott
Connected as scott
SQL> drop table t;
Table dropped
SQL> select * from t;
TABLE_NAME COLUMN_NAME COLUMN_ID
------------------------------ ------------------------------ ----------
PERM X 1
PERM Y 2
PERM Z 3
DEPT DEPTNO 1
DEPT DNAME 2
DEPT LOC 3
EMP EMPNO 1
EMP ENAME 2
EMP JOB 3
EMP MGR 4
EMP HIREDATE 5
EMP SAL 6
EMP COMM 7
EMP DEPTNO 8
BONUS ENAME 1
BONUS JOB 2
BONUS SAL 3
BONUS COMM 4
SALGRADE GRADE 1
------------------------------ ------------------------------ ----------
PERM X 1
PERM Y 2
PERM Z 3
DEPT DEPTNO 1
DEPT DNAME 2
DEPT LOC 3
EMP EMPNO 1
EMP ENAME 2
EMP JOB 3
EMP MGR 4
EMP HIREDATE 5
EMP SAL 6
EMP COMM 7
EMP DEPTNO 8
BONUS ENAME 1
BONUS JOB 2
BONUS SAL 3
BONUS COMM 4
SALGRADE GRADE 1
19 rows selected
然后我创建一个简单的存储过程,对表T中数据进行更新。如果表名为'PERM',就将其列名改为“列名+column_id”的形式。
代码如下:
--script. 1
create or replace procedure p_temp(n_count out number) is
numrows number;--存储column_id
columnnm varchar2(100);--存储column_name
cursor s is
select column_id,column_name
from t
where table_name = 'PERM'
for update of column_name;
begin
n_count:=0;
open s;
loop
fetch s
into numrows,columnnm;
exit when s%notfound;
UPDATE t SET column_name = column_name || numrows WHERE CURRENT OF s;
n_count := n_count + 1;
end loop;
commit;
close s;
end;
代码如下:
--script. 1
create or replace procedure p_temp(n_count out number) is
numrows number;--存储column_id
columnnm varchar2(100);--存储column_name
cursor s is
select column_id,column_name
from t
where table_name = 'PERM'
for update of column_name;
begin
n_count:=0;
open s;
loop
fetch s
into numrows,columnnm;
exit when s%notfound;
UPDATE t SET column_name = column_name || numrows WHERE CURRENT OF s;
n_count := n_count + 1;
end loop;
commit;
close s;
end;
SQL> set serveroutput on
SQL> declare
2 n_count number;
3 begin
4 p_temp(n_count);
5 dbms_output.put_line('更新行数为:'||n_count);
6 end;
7 /
SQL> declare
2 n_count number;
3 begin
4 p_temp(n_count);
5 dbms_output.put_line('更新行数为:'||n_count);
6 end;
7 /
更新行数为:3
PL/SQL procedure successfully completed
SQL> select * from t;
TABLE_NAME COLUMN_NAME COLUMN_ID
------------------------------ ------------------------------ ----------
PERM X1 1
PERM Y2 2
PERM Z3 3
DEPT DEPTNO 1
DEPT DNAME 2
DEPT LOC 3
EMP EMPNO 1
EMP ENAME 2
EMP JOB 3
EMP MGR 4
EMP HIREDATE 5
EMP SAL 6
EMP COMM 7
EMP DEPTNO 8
BONUS ENAME 1
BONUS JOB 2
BONUS SAL 3
BONUS COMM 4
SALGRADE GRADE 1
------------------------------ ------------------------------ ----------
PERM X1 1
PERM Y2 2
PERM Z3 3
DEPT DEPTNO 1
DEPT DNAME 2
DEPT LOC 3
EMP EMPNO 1
EMP ENAME 2
EMP JOB 3
EMP MGR 4
EMP HIREDATE 5
EMP SAL 6
EMP COMM 7
EMP DEPTNO 8
BONUS ENAME 1
BONUS JOB 2
BONUS SAL 3
BONUS COMM 4
SALGRADE GRADE 1
19 rows selected
结果的前三行中column_name更新了。
将存储过程略微调整,改为对表名不等于PERM的数据进行修改:
--script. 2
create or replace procedure p_temp(n_count out number) is
type typ_numrows is table of number;
numrows typ_numrows;
type typ_columnnm is table of varchar2(100);
columnnm typ_columnnm;
cursor s is
select column_id,column_name
from t
where table_name <>'PERM'
for update of column_name;
begin
n_count:=0;
open s;
loop
fetch s bulk collect
into numrows,columnnm limit 3;
exit when s%notfound;
forall i in numrows.first..numrows.last
UPDATE t SET column_name = column_name || numrows(i) WHERE CURRENT OF s;
n_count := n_count + 1;
end loop;
commit;
close s;
end;
我预期的结果是表名不为PERM的数据,column_name后面加上对应的column_id。
但结果却为:
SQL> declare
2 n_count number;
3 begin
4 p_temp(n_count);
5 dbms_output.put_line('fetch次数为:'||n_count);
6 end;
7 /
fetch次数为:5
PL/SQL procedure successfully completed
SQL> select * from t;
TABLE_NAME COLUMN_NAME COLUMN_ID
------------------------------ ------------------------------ ----------
PERM X1 1
PERM Y2 2
PERM Z3 3
DEPT DEPTNO 1
DEPT DNAME 2
DEPT LOC123 3
EMP EMPNO 1
EMP ENAME 2
EMP JOB123 3
EMP MGR 4
EMP HIREDATE 5
EMP SAL456 6
EMP COMM 7
EMP DEPTNO 8
BONUS ENAME781 1
BONUS JOB 2
BONUS SAL 3
BONUS COMM234 4
SALGRADE GRADE 1
------------------------------ ------------------------------ ----------
PERM X1 1
PERM Y2 2
PERM Z3 3
DEPT DEPTNO 1
DEPT DNAME 2
DEPT LOC123 3
EMP EMPNO 1
EMP ENAME 2
EMP JOB123 3
EMP MGR 4
EMP HIREDATE 5
EMP SAL456 6
EMP COMM 7
EMP DEPTNO 8
BONUS ENAME781 1
BONUS JOB 2
BONUS SAL 3
BONUS COMM234 4
SALGRADE GRADE 1
19 rows selected
将上述代码中的
“ forall i in numrows.first..numrows.last
UPDATE t SET column_name = column_name || numrows(i) WHERE CURRENT OF s;”
两行替换为
“delete from t WHERE CURRENT OF s;”
“ forall i in numrows.first..numrows.last
UPDATE t SET column_name = column_name || numrows(i) WHERE CURRENT OF s;”
两行替换为
“delete from t WHERE CURRENT OF s;”
就变成了分批删除数据。
执行结果为:
SQL> declare
2 n_count number;
3 begin
4 p_temp(n_count);
5 dbms_output.put_line('删除操作次数为:'||n_count);
6 end;
7 /
SQL> declare
2 n_count number;
3 begin
4 p_temp(n_count);
5 dbms_output.put_line('删除操作次数为:'||n_count);
6 end;
7 /
删除操作次数为:5
PL/SQL procedure successfully completed
SQL> select * from t;
TABLE_NAME COLUMN_NAME COLUMN_ID
------------------------------ ------------------------------ ----------
PERM X1 1
PERM Y2 2
PERM Z3 3
DEPT DEPTNO 1
DEPT DNAME 2
EMP EMPNO 1
EMP ENAME 2
EMP MGR 4
EMP HIREDATE 5
EMP COMM 7
EMP DEPTNO 8
BONUS JOB 2
BONUS SAL 3
SALGRADE GRADE 1
------------------------------ ------------------------------ ----------
PERM X1 1
PERM Y2 2
PERM Z3 3
DEPT DEPTNO 1
DEPT DNAME 2
EMP EMPNO 1
EMP ENAME 2
EMP MGR 4
EMP HIREDATE 5
EMP COMM 7
EMP DEPTNO 8
BONUS JOB 2
BONUS SAL 3
SALGRADE GRADE 1
14 rows selected
由于在代码中制定了每次提取三行
fetch s bulk collect
into numrows,columnnm limit 3;
n_count := n_count + 1;
end loop;
commit;
close s;
end;
这时候删除的是当前行:
delete from t WHERE CURRENT OF s;
每次提取的最后一行就是当前行,被删除掉了。
end loop;
commit;
close s;
end;
这时候删除的是当前行:
delete from t WHERE CURRENT OF s;
每次提取的最后一行就是当前行,被删除掉了。
因此,除非想要得到上面例子中的特殊效果(每隔几行进行删除或更新操作),
否则,不应该批量操作中出现where current of cursor_name这样的表示(也就是说不要使用script. 2 类似的代码)。
否则,不应该批量操作中出现where current of cursor_name这样的表示(也就是说不要使用script. 2 类似的代码)。
上述代码略显啰嗦,用游标for循环代替:
--script. 3
create or replace procedure p_test is
n_count number:=0;
CURSOR t_cur IS
SELECT * FROM t FOR UPDATE;
BEGIN
FOR t_row IN t_cur
LOOP
IF t_row.table_name='PERM' THEN
UPDATE t SET t.column_name=t.column_name||t.column_id WHERE CURRENT OF t_cur;
n_count:=n_count+1;
ELSE
null;
END IF;
END LOOP;
dbms_output.put_line(n_count);
END;
--script. 3
create or replace procedure p_test is
n_count number:=0;
CURSOR t_cur IS
SELECT * FROM t FOR UPDATE;
BEGIN
FOR t_row IN t_cur
LOOP
IF t_row.table_name='PERM' THEN
UPDATE t SET t.column_name=t.column_name||t.column_id WHERE CURRENT OF t_cur;
n_count:=n_count+1;
ELSE
null;
END IF;
END LOOP;
dbms_output.put_line(n_count);
END;
至于使用where current of的好处,通过以上几个例子也有所体会。
可能实际用到的存储过程会是这样:
--script. 4
create or replace procedure p_temp(table_nm varchar2, n_count out number) is
numrows number; --存储column_id
columnnm varchar2(100); --存储column_name
cursor s is
select column_id, column_name
from t
where table_name = table_nm
for update of column_name;
begin
n_count := 0;
可能实际用到的存储过程会是这样:
--script. 4
create or replace procedure p_temp(table_nm varchar2, n_count out number) is
numrows number; --存储column_id
columnnm varchar2(100); --存储column_name
cursor s is
select column_id, column_name
from t
where table_name = table_nm
for update of column_name;
begin
n_count := 0;
open s;
loop
fetch s
into numrows, columnnm;
exit when s%notfound;
UPDATE t SET column_name = column_name || numrows WHERE CURRENT OF s;
n_count := n_count + 1;
end loop;
commit;
close s;
end;
loop
fetch s
into numrows, columnnm;
exit when s%notfound;
UPDATE t SET column_name = column_name || numrows WHERE CURRENT OF s;
n_count := n_count + 1;
end loop;
commit;
close s;
end;
其实就是在“script. 1”基础上把table_name的值作为输入参数了。
这样,每次更新或删除操作都只是局部的,相对于全局的update或delete操作来说范围小,处理快,被锁住的时间短。
这是where current of的好处。
这样,每次更新或删除操作都只是局部的,相对于全局的update或delete操作来说范围小,处理快,被锁住的时间短。
这是where current of的好处。
另外有一点比较容易忽略:在script. 4中不要试图在update语句后立即commit。
这样会导致锁的释放,后续的fetch操作就找不到current of s的列,因此报错“ORA-01002: 提取违反顺序”
这样会导致锁的释放,后续的fetch操作就找不到current of s的列,因此报错“ORA-01002: 提取违反顺序”
参考:
http://www.itpub.net/thread-1798767-1-2.html
http://hi.baidu.com/8kevinlj/item/69c1680b1c583538f3eafc49
http://www.itpub.net/thread-1798767-1-2.html
http://hi.baidu.com/8kevinlj/item/69c1680b1c583538f3eafc49
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26451536/viewspace-764967/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/26451536/viewspace-764967/