最近接到一家用户的反映,说在对数据进行逻辑备份时,每次备份到“门诊费用记录”表就会提示0RA-8103错误,导致该表数据无法备份,前方的技术人员已经处理了很久,都无法排除这个故障。
我们都知道“门诊费用记录”表是在ZLHIS系统的关键数据,如果这张表没有备份出来就相当于备份失效,用户希望高级技术支持部能够排除该问题,使得备份有效,避免数据丢失。
分析过程:首先远程到用户处查看现象,确实如描述,数据库在进行全库导出的时候,其他表都能够正常导出,只有“门诊费用记录“表提示错误ora-08103错误,首先我们还是先看下一错误代码的解释:
08103, 00000, “object no longer exists”
// *Cause: The object has been deleted byanother user since the operation
// began, or a prior incomplete recoveryrestored the database to
// a point in time during the deletion of theobject.
// *Action: Delete the object if this is theresult of an incomplete
// recovery.
从错误代码的官方解释可以看出,问题还是比较严重,查询资料得到更详细的解释如下:
Theheader block has an invalid block type or the block type inside the block is not expected. i.e. A data block (Type=6) was expected but theactual block information is not a datablock.
Or
Thedata_object_id (seg/obj) stored in the block is different than the data_object_id stored in the segment header
大体意思是数据块头的信息与数据段头的信息不一致,解决的方式要么删除这个对象,要么做不完全恢复。彻底删除这个对象肯定不是我们想要的结果,但是由于数据库未开归档,日常也无法通过rman做不完全恢复。
我们还是回到问题本身,首先目前用户还在正常使用,证明“门诊费用记录”表并不是完全无法使用,在plsql中通过select * from查询,能够得到查询结果也证明了我们的判断,但是通过select count(*) from全表扫描,就会提示ora-08103错误,证明该数据表中只是部分数据出现问题,因此我们是不是可以尝试把“门诊费用记录”表中正常的数据提取出来,这样至少可以最大承担的挽回损失。
解决过程:由于“门诊费用记录”表已经损害,因此我们尝试创建一张结构完全一样的新表,把正常的数据迁移到新表中。
1. 按照创建一个新表,结构与“门诊费用记录“表完全相同
SQL> Create Table 门诊费用记录new as select * from门诊费用记录where 1=2
2 /
Table created
2. 创建一个新表用来存储损坏记录的信息,方便后续操作
SQL> create table bad_rows
2 ( row_id rowid,
3 Id NUMBER(18) NOT NULL
4 );
Table created
3. 由于全表扫描提出错误,因此在查询的时候,不能走全表扫描,尽可能走索引,这里就需要找到一个非空字段的索引,目的是通过非空字段索引对表进行搜索,通过分析“门诊费用记录”发现存在一个索引“门诊费用记录_PK”。
4. 编写循环代码,一条条的取出所有没有损坏的数据,并把有问题的块中的数据的rowid,id两个字段存储到BAD_ROWS表中,详细的代码如下:
set serveroutput on
declare
nrows number;
badrows number;
id_in NUMBER(18);
begin
badrows:=0;
nrows:=0;
for i in (select /*+ index (门诊费用记录_PK) */ rowid,id from 门诊费用记录 tab1) loop
begin
insert into 门诊费用记录new select *
from 门诊费用记录where rowid=i.rowid;
if (mod(nrows,20000)=0) then commit;end if;
exception when others then
badrows:=badrows+1;
select /*+ index(门诊费用记录_PK) */ id into id_in from 门诊费用记录 a where rowid=i.rowid;
insert into bad_rows values(i.Rowid,id_in);
commit;
end;
nrows:=nrows+1;
end loop;
dbms_output.put_line('Total rows:'||to_char(nrows)||' Bad rows: '||to_char(badrows));
Commit;
end;
/
通过执行上面的过程,把所有正常的记录转移到了门诊费用记录new中,同时把有问题的记录rowid存储在bad_rows表中,我们这里的情况是bad_rows表中有15条数据,“门诊费用记录new”表中被转移了10W条数据,接下来就是对问题表重名,然后把”门诊费用记录new”表重命名为”门诊费用记录”表,同时在上面创建相关的索引和约束,对表进行授权。
Sql>alter table门诊费用记录rename to门诊费用记录old;
Table altered
Sql>alter table门诊费用记录new rename to门诊费用记录;
Table altered