一个有趣的错误ORA-1002
2014-10-09 16:34
47 查看
来自:http://blog.itpub.net/4227/viewspace-202018/
今天同事发现了一个错误,错误号为ORA-1002。
为了更好展示这个问题,构造了下面的代码了重现问题:
SQL> CREATE TABLE T (ID NUMBER);
表已创建。
SQL> CREATE TABLE T1 (ID NUMBER);
表已创建。
SQL> INSERT INTO T VALUES (1);
已创建 1 行。
SQL> INSERT INTO T1 SELECT ROWNUM FROM TAB WHERE ROWNUM < 4;
已创建3行。
SQL> COMMIT;
提交完成。
SQL> SELECT * FROM T;
ID
----------
1
SQL> SELECT * FROM T1;
ID
----------
1
2
3
下面只需要执行下面的PL/SQL语句,就可以重现问题:
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 FOR I IN (SELECT ID FROM T1) LOOP
4 IF I.ID = 2 THEN
5 ROLLBACK;
6 END IF;
7 END LOOP;
8 END;
9 /
BEGIN
*
第 1 行出现错误:
ORA-01002: 读取违反顺序
ORA-06512: 在line 3
看来是由于ROLLBACK语句影响了FOR循环中CURSOR的状态。如果去掉ROLLBACK语句或者去掉FOR语句前面的UPDATE语句,都是不会报错的。
SQL> BEGIN
2 FOR I IN (SELECT ID FROM T1) LOOP
3 IF I.ID = 2 THEN
4 ROLLBACK;
5 END IF;
6 END LOOP;
7 END;
8 /
PL/SQL 过程已成功完成。
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 FOR I IN (SELECT ID FROM T1) LOOP
4 IF I.ID = 2 THEN
5 NULL;
6 END IF;
7 END LOOP;
8 END;
9 /
PL/SQL 过程已成功完成。
SQL> ROLLBACK;
回退已完成。
如果在UPDATE语句后面添加COMMIT,也是不会报错的:
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 COMMIT;
4 FOR I IN (SELECT ID FROM T1) LOOP
5 IF I.ID = 2 THEN
6 ROLLBACK;
7 END IF;
8 END LOOP;
9 END;
10 /
PL/SQL 过程已成功完成。
基本上可以确认问题是由于ROLLBACK需要回滚CURSOR之前的DML,导致Oracle改变了CURSOR本身的状态。
SQL> SELECT * FROM V$VERSION;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
PL/SQL Release 9.2.0.4.0 - Production
CORE 9.2.0.3.0 Production
TNS for 32-bit Windows: Version 9.2.0.4.0 - Production
NLSRTL Version 9.2.0.4.0 - Production
这个问题发生在9204,10g的FOR循环解决了这个问题:
SQL> CONN YANGTK/YANGTK@YTK102
已连接。
SQL> SELECT * FROM V$VERSION;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production
SQL> CREATE TABLE T (ID NUMBER);
表已创建。
SQL> CREATE TABLE T1 (ID NUMBER);
表已创建。
SQL> INSERT INTO T VALUES (1);
已创建 1 行。
SQL> INSERT INTO T1 SELECT ROWNUM FROM TAB WHERE ROWNUM < 4;
已创建3行。
SQL> COMMIT;
提交完成。
SQL> SELECT * FROM T;
ID
----------
1
SQL> SELECT * FROM T1;
ID
----------
1
2
3
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 FOR I IN (SELECT ID FROM T1) LOOP
4 IF I.ID = 2 THEN
5 ROLLBACK;
6 END IF;
7 END LOOP;
8 END;
9 /
PL/SQL 过程已成功完成。
不过,只需要将FOR循环游标改为用户声明并FETCH的游标,文件就会重新:
SQL> DECLARE
2 CURSOR C_CURSOR IS SELECT ID FROM T1;
3 I C_CURSOR%ROWTYPE;
4 BEGIN
5 UPDATE T SET ID = ID;
6 OPEN C_CURSOR;
7 FETCH C_CURSOR INTO I;
8 LOOP
9 EXIT WHEN C_CURSOR%NOTFOUND;
10 IF I.ID = 2 THEN
11 ROLLBACK;
12 END IF;
13 FETCH C_CURSOR INTO I;
14 END LOOP;
15 CLOSE C_CURSOR;
16 END;
17 /
DECLARE
*
第 1 行出现错误:
ORA-01002: 提取违反顺序
ORA-06512: 在 line 13
10G中虽然修正了这个bug,但是修改的并不彻底。在11g中,这个问题和10g中一样。在Metalink上也没有看到Oracle对这个bug有相关的描述。
这个bug也很容易避免,除了上面的几种写法外,推荐一种更合理的做法:
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 FOR I IN (SELECT ID FROM T1) LOOP
4 IF I.ID = 2 THEN
5 RAISE_APPLICATION_ERROR(-20000, 'USER_ERR');
6 END IF;
7 END LOOP;
8 EXCEPTION
9 WHEN OTHERS THEN
10 ROLLBACK;
11 END;
12 /
PL/SQL 过程已成功完成。
这才是一种合理的异常处理方法,而例子中采用的在循环中回滚的方式本身就是不推荐的。
复制去Google翻译翻译结果
今天同事发现了一个错误,错误号为ORA-1002。
为了更好展示这个问题,构造了下面的代码了重现问题:
SQL> CREATE TABLE T (ID NUMBER);
表已创建。
SQL> CREATE TABLE T1 (ID NUMBER);
表已创建。
SQL> INSERT INTO T VALUES (1);
已创建 1 行。
SQL> INSERT INTO T1 SELECT ROWNUM FROM TAB WHERE ROWNUM < 4;
已创建3行。
SQL> COMMIT;
提交完成。
SQL> SELECT * FROM T;
ID
----------
1
SQL> SELECT * FROM T1;
ID
----------
1
2
3
下面只需要执行下面的PL/SQL语句,就可以重现问题:
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 FOR I IN (SELECT ID FROM T1) LOOP
4 IF I.ID = 2 THEN
5 ROLLBACK;
6 END IF;
7 END LOOP;
8 END;
9 /
BEGIN
*
第 1 行出现错误:
ORA-01002: 读取违反顺序
ORA-06512: 在line 3
看来是由于ROLLBACK语句影响了FOR循环中CURSOR的状态。如果去掉ROLLBACK语句或者去掉FOR语句前面的UPDATE语句,都是不会报错的。
SQL> BEGIN
2 FOR I IN (SELECT ID FROM T1) LOOP
3 IF I.ID = 2 THEN
4 ROLLBACK;
5 END IF;
6 END LOOP;
7 END;
8 /
PL/SQL 过程已成功完成。
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 FOR I IN (SELECT ID FROM T1) LOOP
4 IF I.ID = 2 THEN
5 NULL;
6 END IF;
7 END LOOP;
8 END;
9 /
PL/SQL 过程已成功完成。
SQL> ROLLBACK;
回退已完成。
如果在UPDATE语句后面添加COMMIT,也是不会报错的:
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 COMMIT;
4 FOR I IN (SELECT ID FROM T1) LOOP
5 IF I.ID = 2 THEN
6 ROLLBACK;
7 END IF;
8 END LOOP;
9 END;
10 /
PL/SQL 过程已成功完成。
基本上可以确认问题是由于ROLLBACK需要回滚CURSOR之前的DML,导致Oracle改变了CURSOR本身的状态。
SQL> SELECT * FROM V$VERSION;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
PL/SQL Release 9.2.0.4.0 - Production
CORE 9.2.0.3.0 Production
TNS for 32-bit Windows: Version 9.2.0.4.0 - Production
NLSRTL Version 9.2.0.4.0 - Production
这个问题发生在9204,10g的FOR循环解决了这个问题:
SQL> CONN YANGTK/YANGTK@YTK102
已连接。
SQL> SELECT * FROM V$VERSION;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production
SQL> CREATE TABLE T (ID NUMBER);
表已创建。
SQL> CREATE TABLE T1 (ID NUMBER);
表已创建。
SQL> INSERT INTO T VALUES (1);
已创建 1 行。
SQL> INSERT INTO T1 SELECT ROWNUM FROM TAB WHERE ROWNUM < 4;
已创建3行。
SQL> COMMIT;
提交完成。
SQL> SELECT * FROM T;
ID
----------
1
SQL> SELECT * FROM T1;
ID
----------
1
2
3
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 FOR I IN (SELECT ID FROM T1) LOOP
4 IF I.ID = 2 THEN
5 ROLLBACK;
6 END IF;
7 END LOOP;
8 END;
9 /
PL/SQL 过程已成功完成。
不过,只需要将FOR循环游标改为用户声明并FETCH的游标,文件就会重新:
SQL> DECLARE
2 CURSOR C_CURSOR IS SELECT ID FROM T1;
3 I C_CURSOR%ROWTYPE;
4 BEGIN
5 UPDATE T SET ID = ID;
6 OPEN C_CURSOR;
7 FETCH C_CURSOR INTO I;
8 LOOP
9 EXIT WHEN C_CURSOR%NOTFOUND;
10 IF I.ID = 2 THEN
11 ROLLBACK;
12 END IF;
13 FETCH C_CURSOR INTO I;
14 END LOOP;
15 CLOSE C_CURSOR;
16 END;
17 /
DECLARE
*
第 1 行出现错误:
ORA-01002: 提取违反顺序
ORA-06512: 在 line 13
10G中虽然修正了这个bug,但是修改的并不彻底。在11g中,这个问题和10g中一样。在Metalink上也没有看到Oracle对这个bug有相关的描述。
这个bug也很容易避免,除了上面的几种写法外,推荐一种更合理的做法:
SQL> BEGIN
2 UPDATE T SET ID = ID;
3 FOR I IN (SELECT ID FROM T1) LOOP
4 IF I.ID = 2 THEN
5 RAISE_APPLICATION_ERROR(-20000, 'USER_ERR');
6 END IF;
7 END LOOP;
8 EXCEPTION
9 WHEN OTHERS THEN
10 ROLLBACK;
11 END;
12 /
PL/SQL 过程已成功完成。
这才是一种合理的异常处理方法,而例子中采用的在循环中回滚的方式本身就是不推荐的。
复制去Google翻译翻译结果
相关文章推荐
- Oracle学习笔记:一个特殊的ORA-12541错误原因
- 今天在一个10203数据库的alert文件中发现了ORA-1461的错误。
- 一个有趣的 ”Validation of viewstate MAC failed” 错误的发现和解决
- SQL语句结尾处多加了一个分号,导致出现 ------ORA-00911: 无效字符---错误
- Oracle11g 遇到的一个ORA-00600[kcratr_scan_lastbwr]错误处理
- Oracle关于imp的一个错误ORA-00907的解决方案
- ORA-12641 错误的一个解决办法
- 之前创建了一个表空间,然后今天不小心把表空间文件给删除了。然后用pl/sql连接数据库的时候,报ORA-01033错误。ora-01033:oracle initializationg or shut
- 手工建库启动实例时一个小错误:ORA-00824: cannot set sga_target due to existing internal settings
- 虚拟机部署度量快速开发平台服务端出现ora-27101错误的一个解决方法
- 今天在一个10203数据库的alert文件中发现了ORA-1461的错误。
- ORA-00917: 缺失逗号 一个小错误恶心了我一个小时
- 今天在一个10203数据库的alert文件中发现了ORA-1461的错误。
- 做dataguard犯了一个低级错误修改参数报错ORA-16187: LOG_ARCHIVE_CONFIG contains duplicate, conflicting or invalid att
- ORA-00988: 口令缺失或无效[oracle 10g会有的一个错误]
- 由ORA-00979错误发现ORACLE一个BUG
- 一个空格导致的ORA-12516错误
- ORA-12641 错误的一个解决办法
- 11gR2RAC环境DBCA创建一个数据库错误ORA-15055 ORA-15001
- ALERT日志中常见监听相关报错之中的一个:ORA-609错误的排查