ORA-1555经典的错误(二)

2014-11-24 17:04:11 · 作者: · 浏览: 1
gather_table_stats(user, 'T', cascade=>true);
PL/SQL procedure successfully completed.

重复更新表中所有数据。

SQL> begin
for x in (select rowid rid from t)
loop
update t set object_name = lower(object_name) where rowid = x.rid;
commit;
end loop;
end;
/
PL/SQL procedure successfully completed.

上述语句执行过程中,创建查询语句,这里使用DBMS_LOCK.SLEEP(0.01)来模拟查询单次时间是0.01秒,由于是随机插入到表中的,因此此处相当于随机地查询表中的块。这个查询语句执行几秒就可能失败。
declare
cursor c is
select /*+first_rows*/ object_name
from t
order by object_id;

l_object_name t.object_name%type;
l_rowcnt number:=0;
begin
open c;
loop
fetch c into l_object_name;
exit when c%notfound;
dbms_lock.sleep(0.01);
l_rowcnt := l_rowcnt + 1;
end loop;
close c;
exception
when others then
dbms_output.put_line('rows fetched = '|| l_rowcnt);
raise;
end;
/
(注:报错

l_rowcnt number:=0;
*
ERROR at line 8:
ORA-06550: line 8, column 4:
PLS-00103: Encountered the symbol "L_ROWCNT" when expecting one of the
following:
:= ( ; not null range default character
The symbol ";" was substituted for "L_ROWCNT" to continue.
可能是l_object_name t.object_name%type少分号)

报错是:

declare
*
ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number 11 with name "_SYSSMU11$" too small
ORA-06512: at line 21

总结:
对于报错,原因在于SELECT语句是按照object_id的索引进行读取,(INDEX FULL SCAN),由于之前数据是按照随机顺序插入得到的,因此此处是在全表上执行随机读,这样就可能出现:SELECT读到的数据可能是不同块中的,此时UPDATE更新数据并提交,标识UNDO中该记录可被覆盖,由于UNDO空间较小,因此出现SELECT读取的UNDO块被UPDATE更新、提交而被覆盖的可能性就会变大,于是出现了ORA-1555的错误。

为了解决这种问题,将UNDO设置为可扩展,让Oracle自动管理UNDO,可以最大幅度地扩展UNDO容量,满足UNDO_RETENTION时间要求的同时,也保证了SELECT可以读到的块不会被UPDATE提交所覆盖。
SQL> column file_name new_val F
SQL> select file_name from dba_data_files where tablespace_name='UNDO_SMALL';
FILE_NAME
--------------------------------------------------------------------------------
/opt/oracle/oradata/bisal/undo_small.dbf

SQL> Alter database datafile '&F' autoextend on
2 next 1m
3 maxsize 2048m;
old 1: Alter database datafile '&F' autoextend on
new 1: Alter database datafile '/opt/oracle/oradata/bisal/undo_small.dbf' autoextend on
Database altered.

SQL> select bytes/1024/1024 from dba_data_files where tablespace_name='UNDO_SMALL';
BYTES/1024/1024
---------------
4

此时再执行上述UPDATE和SELECT语句,即可执行完成。