阿的ORACLE数据库里有这么2套表,一套为表名后缀为“_OLD”的表,存放老数据【记做老表】;另一套为结构完全相同的表名去掉OLD的表存放新数据【记做新表】。
    写了这样一个过程,实现功能如下:把OLD表清空,并把新表中的数据搬到老表中来;最后把新表清空,以便以后的过程往里面进数据。
    新老表每套表为20张,为了节省代码,过程里面用了个游标从TAB里来取表名。主要代码如下:
-----------------------------BENGIN--------------------------------------
声明部分:/*游标的声明,tname为老表,另一字段是取新表名*/
CURSOR C_table_name IS
      SELECT tname,SUBSTR(tname,1,INSTR(tname,'_OLD')-1) FROM TAB 
         WHERE tname like 'T%_OLD';
------------------------------------
执行部分:  OPEN C_table_name;
  LOOP
    BEGIN           
      v_sqlstr1 := 'INSERT INTO ';
      v_sqlstr2 := 'TRUNCATE TABLE ';
      FETCH C_table_name INTO v_table_old,v_table;
      EXIT WHEN C_table_name%NOTFOUND;
      v_sqlstr2 := v_sqlstr2||v_table_old;
      v_sqlstr1 := v_sqlstr1||v_table_old||' SELECT * '||'FROM '||v_table;
      EXECUTE IMMEDIATE v_sqlstr2;
      EXECUTE IMMEDIATE v_sqlstr1;
      COMMIT;
      --一个表数据一提交
      
      v_sqlstr3 := 'TRUNCATE TABLE '||v_table;
      EXECUTE IMMEDIATE v_sqlstr3;
      --在此先清理新表以便下步对新表进行插入时不用删除
      
    EXCEPTION
      WHEN OTHERS THEN
   ROLLBACK;
   v_err := 'error:'||v_table_old||sqlerrm||sqlcode;
   v_res := -1;
    END;
   END LOOP;
  CLOSE C_table_name;
-------------------------------END---------------------------------------
    过程实现功能也比较简单,应该说这个过程结构清晰,没有什么逻辑上的错误;在执行的过程中奇怪的问题暴露出来了,这个循环执行的过程中,执行到某个表的时候【即游标FETCH到某个表】,这个过程就陷入了异常的“死循环”里面{好像跳不出来}!!!
报错记录如下:          
error:{此处省略TABLE名}ORA-01555: 快照过旧: 回退段号 1 在名称为 "ETL02" 过小-1555
error:{此处省略TABLE名}ORA-01002: 读取违反顺序    ORA-01002号错误应该是由ORA-01555引起的,ORA-01555号错误的产生好像是快照过旧造成的;不过,阿还可以肯定的是,在这个过程执行的过程中,没有其它用户对这些表进行写操作,不存在快照过旧的因素!!并且,对于这种情况,调整回滚段应该是不符合实际情况的!翻阅资料和问其它精通ORACLE的人,他们对这个过程中产生ORA-01555号错误基本上不能够给出合理的解释,这种情况下ORA-01555号错误好像是无法解决的!?
   但是,在这个过程里面到底是原因引起了ORA-01555号错误呢?阿感觉好像是由于游标要循环20次,在游标里进行多次提交可能会是引起这种错误的原因?更加奇怪的是这个过程阿调试了3次,操作都是进行到同一个表的时候陷入了异常的“死循环”里!!!大家有谁碰到过如此问题,不妨讨论一下~

解决方案 »

  1.   

    更蹊跷的情况是这样:阿记录到每次报错时那个表的表名,然后重新写了个过程,在声明的游标处加上条件,来限制游标从那个报错的过程未处理的那个表开始继续往下处理,结果是成功执行!!!
    这个情况实在想不通了~!其实要把这个功能实现其实并不难,阿把游标去掉,然后在SQL语句里面把表名写死,分情况提交数据就OK了,只是想搞清楚这个错误在过程里面到底是怎么产生的?
      

  2.   

    加大回滚段,
    或者修改程序,改为整体提交,不要分步提交snapshot too old与是否一个人使用无关,可能因为你的后续操作要用到前面的数据,
    你分步提交后,回滚段自动收缩时,会截掉非活动事务的数据
      

  3.   

    1. 出错的那张表的记录数是否很多?
    2. 那张表是否建有约束?建有约束的使用TRUNCATE会有些问题。
      

  4.   

    TO_ dongxiangjun() :
    因为表的数目较多【20张】并且有些表的数据量比较大【都是几十万的】,所以整体提交一个是锁表较多,耗费资源;另外,回滚段也可能会不够用【当然,前提是在现有回滚段大小情况下】TO_Lastdrop(空杯):
    出错的那张表确实数据量很大!之前的表数据量都是比较小的,不过也有几张大表。
    表是没有约束的。目前,这个问题已经解决,虽然解决,但是原因好像并没有搞清!
    问题是这样解决的:
    游标声明处,不再对ORACLE数据字典进行操作,而是把想要的表名存放到一中间表中;
    在这些表名放到中间表的时候,先放入数据量大的表名,即在操作中先处理数据量大的,这样改过后,目前看过程执行顺利!但是,问题的原因难道就是因为游标中使用了ORACLE的数据字典了么?那么,又怎么会ORA-01555号错误呢?搞不懂!
      

  5.   

    不是Oracle的使用数据字典的问题,就是因为数据量大,回滚段使用和回收的问题,你可以同样使用Oracle的数据字典,只不过还是先处理数据量大的表,也会执行成功的。
      

  6.   

    回滚段太小了,因为你的事务还没有完成而回滚段的空间已经没有了。
    CREATE ROLLBACK SEGMENT rbs_temp STORAGE (INITIAL 10M NEXT 10M OPTIMAL 20M)
    ALTER ROLLBACK SEGMENT rbs_temp ONLINE
    在你的过程中
    set transaction use rollback segment rbs_temp
      

  7.   

    TO_ woodgl()  qxm(qxm) :
    由于ORACLE报的错误号是ORA-01555,虽说这个错误是与回滚段有关的,但是这个错误不是因为回滚段太小引起的,应该说回滚段大小不是这个问题产生的原因,所以在思考解决这个问题的过程中没有考虑回滚段大小的影响。