如果自己搞不定可以找诗檀软件专业ORACLE数据库修复团队成员帮您恢复!

诗檀软件专业数据库修复团队

服务热线 : 13764045638 QQ号:47079569 邮箱:service@parnassusdata.com

    从Oracle7的表中挽救数据 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本文是 Note:28814.1的延伸,讨论了应对 Oracle中块损坏的可用选择,这里我们讨论在 Oracle7版本中如何从包含一个损坏块或若干损坏块的表中提取数据。 *** 本文不涵盖Oracle8 - 如需要,请参考 Note:61685.1*** 为了使用这些步骤,在这里需要列举表中所有的损坏文件和损坏块,最好能获取下列信息: - Original Error - File# - Referred to as <AFN> in this article - Block# - Referred to as <BL> in this article - Object Type - eg: TABLE or CLUSTER - Object Owner.Name - Related Objects - eg: Indexes, Foreign key constraints etc... 如果没有,请参考 Note:28814.1来详细了解如何获获取和谐信。 =========== <A> 选项 =========== 从损坏表中提取数据有很多方法: (1) 最简单的首要选择: - Oracle 7.2 及更高版本存在一个Oracle 事件,用于跳过损坏块, 这是目前提取表中数据最简单的选择,在 Note:33405.1有所讨论,注意,如果损坏块报告为ORA-1578,则只可以使用该事件。 (2) 第二种选择 (更复杂): - 从 Oracle 7.1开始可以使用 ROWID范围扫描,这种方法会在本文中有所讨论。 (3) 第三种选择 (更耗时): - 如果有一个主键,你可以通过这个索引中选择表数据。通过其它任何索引选择数据或许也是可行的,这种方法作用慢,比较耗时,这种方法在本文中也会有所讨论,但在现在的 Oracle版本中一般不需要。 如果上述的三种选择失败或者不可能进行,那么就会有第四种选择: - 有很多挽救程序 / PLSQL 脚本,用于完就表中数据,比起上述方法,建立和使用可能需要更长时间,但是可用于应对各种各样的损坏。 列举在 Note:28814.1 ,此处不涉及。 在查看提取数据之前,需要了解'ROWID'是什么,这在一开始就讨论了,接下来讨论下列话题: <C> Rowid 范围扫描 <D> 通过索引从表中获取数据 <E> 可能的问题 <F> 丢失了什么数据? ================== <B> Oracle7 ROWIDs ================== *** 注意,这里的信息只适用于Oracle7. 数据库表中的每个行都可以被一个 'ROWID'虚拟数据行唯一引用, rowid是数据库中行位置的十六进制表示: <BL>.ROW-SLOT.<AFN> 例如:如果我们发出SELECT EMPNO, ROWID FROM EMP ,可能会得 到: EMPNO ROWID ----- ----- 100 00000003.0000.000A 101 00000003.0001.000A ... etc Hence these 2 rows are in file 0xA which is 10 decimal, block 3. 对任何坏块,我们可以通过将<AFN>和<BL>从误差转换成十六进制,使用上述ROWID格式来制作一个模版ROWID: 例如: 对文件 11, 块 22 ,我们有: <AFN> = File 11 == B hex <BL> = Block 22 == 16 hex 该块中rowid的行数从 00000016.0000.000B 到 00000016.7FFF.000B,因为最大的行槽数是7FFF hex。 ===================== <C> Rowid 范围扫描 ===================== 从Oracle7.1开始,可以使用ROWID范围扫描提示从基表中选择数据。这使得可以围绕一个损坏块选择。 该命令的句法很简单: SELECT /*+ ROWID(tablename) */ column list FROM tablename WHERE rowid ... WHERE子句必须做一个ROWID范围比较,如: WHERE rowid BETWEEN 'low-rowid' and 'high-rowid'; 我们可以多次使用这种类型的语句,围绕坏块选择数据,插入到一个挽救表中,注意创建 "WHERE" 子句时,不能直接参考损坏快中的ROWID,而是使用损坏块的 ROWID的两侧。 例如: SQL> SELECT count(*) FROM bigemp; ERROR: ORA-01578: ORACLE data block corrupted (file # 8, block # 8147) ORA-01110: data file 8: '/oracle/m4/v714/instance/dbs/usr3714.dbf' Corrupt File ID <AFN>: 8 == 8 Hex Corrupt Block ID <BL>: 8147 == 1FD3 Hex 于是: First ROWID in the corrupt block: 00001FD3.0000.0008 Last possible ROWID before this block: 00001FD2.7FFF.0008 First ROWID after this block: 00001FD4.0000.0008 (最后的 rowid应该都是 bno.7FFF.fno) 因此我们: 创建放置数据的副本表: CREATE TABLE salvage AS SELECT * FROM bigemp WHERE 1 = 2; 在损坏块前插入数据: INSERT INTO salvage SELECT /*+ ROWID(bigemp) */ * FROM bigemp WHERE rowid <= '00001FD2.7FFF.0008'; 在损坏块后插入数据: INSERT INTO salvage SELECT /*+ ROWID(bigemp) */ * FROM bigemp WHERE rowid >= '00001FD4.0000.0008'; 注意我们在损坏块的 rowid'两侧使用的 <= and >= with,如果在损坏块中包括ROWID ,就会产生错误。 如果一系列块发生损坏,则需要在范围比较中适当修改 rowids,如果表中不同的部分出现快损坏,则需要若干插入语句。 ============================================== <D> 通过索引从表中获取数据 ============================================== 在 Oracle 7.1版本之前,此选项很有用,因为不存在ROWID范围扫描,假设有带有列EMPNO, ENAME和DEPTNO的同样的损坏表 BIGEMP,那么在EMPNO列上就会唯一的索引;假设索引也未损坏,我们正在寻找一个not null列,我们可以通过索引从表中选择数据,因为该索引适用于表中的每一行的ROWID。 例如: SELECT rowid, empno FROM bigemp WHERE empno>=0; ROWID EMPNO ------------------ ---------- 00001FC3.000C.0008 177 00001FC3.000D.0008 178 00001FC3.0010.0008 181 00001FC3.0011.0008 182 ... 我们使用EMPNO>=0 启用索引,这不会出错,因为上面的查询从缩阴开始完全符合。 注意,如果指数均在字符列,我们将把上面的where 子句改为类似于 WHERE EMPNO > ', 由于索引符合ROWID,我们可以使用在WHERE子句中使用一个过滤,以确定坏块中索引列的值,我们用以下形式的where子句: ROWIDTOCHAR( ROWID ) LIKE '<BL>.%.<AFN>' 例如: 对损坏块和损坏文件使用以前的值: SQL> SELECT empno from BIGEMP WHERE EMPNO >= 0 AND ROWIDTOCHAR(ROWID) LIKE '00001FD3.%.0008'; EMPNO ---------- 821 822 .... 861 如果只有几个行或它们是连续的值,可以使用以下子句选出数据: WHERE empno <821 例如: 创建一个副本表: CREATE TABLE TEMP AS SELECT * FROM BIGEMP WHERE 1 = 2; 然后围绕损坏插入到表中: INSERT INTO TEMP SELECT * FROM BIGEMP WHERE EMPNO < 821; INSERT INTO TEMP SELECT * FROM BIGEMP WHERE EMPNO > 861; 如果损坏块中有很多行或者索引的值是不连续的,则需要使用一个更复杂的提取方法: 例如: 为获得我们想要提取的一系列关健值: CREATE TABLE pk_table AS SELECT empno FROM bigemp WHERE empno >= 0 AND ROWIDTOCHAR(ROWID) NOT LIKE '00001FD3.%.0008'; 现在创建目的表: CREATE TABLE temp AS SELECT * FROM bigemp WHERE 1 = 2; 插入数据: INSERT INTO temp SELECT bigemp.* FROM bigemp, pk_table WHERE bigemp.empno >= 0 AND bigemp.empno = pk_table.empno; ===================== <E>可能的问题 ===================== 链接行: 行作为链接行或迁移行是有可能的,这样的行跨越多个Oracle块,该'ROWID'是行的第一个部分(或“头”)的位置。如果包含一个链接行和“头”块的块出现损坏,则需要调整上述程序,不但排除损坏,也要排除损坏块中有部分行片的任何行,这种行可以通过使用相关的WHERE 子句从表中选择 'ROWID , TABLE.*'来确定,以规避已知的损坏块。如果收到错误,那么通过该查询返回的最后一个行应该在有损坏块部分内容的行之前显示ROWID,也应该调整上述相关查询以避免链接行和迁移行,在不出错的情况下提取可用数据之前,创建'salvage' 查询可能需要多次尝试。 Long 列: LONG 列存在限制,因为只有特定的 SQL 操作可以进行,可以使用上面描述的相同方法提取数据,但是真正复制数据可能需要使用SQLPLUS 'COPY' 命令,需要注意的是sqlplus的副本有一个端口具体限制你可以复制LONG的最大尺寸,参考SQLPLUS User Guide和端口具体文件确定是否可行。 =========================== <F> 丢失了什么数据? =========================== 如果在损坏表上有索引,使用上面描述的 'Index Scan'方法可以确定丢失的数据,假设有带有列EMPNO, ENAME和DEPTNO的同样的损坏表 BIGEMP,那么在EMPNO列上就会唯一的索引,可以从损坏块索引中选择数据: SQL> SELECT empno from BIGEMP WHERE EMPNO >= 0 AND ROWIDTOCHAR(ROWID) LIKE '00001FD3.%.0008'; 其中,“EMPNO>= 0”强制索引范围扫描,“ROWIDTOCHAR(ROWID)LIKE'00001FD3%。0008'”将结果限制为这些条目会出现在坏块上。 可以在表的所有索引上使用这种方法,以获取丢失数据尽可能多的信息。