服务器 频道

DBMS_REPAIR包修复损坏数据块

2 恢复数据

使用DBMS_REPAIR包的目的不仅是为了使表重新可以访问,而且使用这个包还能在一定程度上恢复被因坏块而无法读取的数据。

由于坏块产生在表上,因此索引是可以访问,所有被索引的列的数据都可以恢复。遗憾的是,Oracle的文档并没有给出恢复的方法,我查询了METALINK和ASKTOM也没有找到相应的答案,所以,恢复的工作只能靠自己摸索进行。这部分的内容完全是建立在我对Oracle数据类型理解的基础上的,虽然我已经对我的程序进行过测试,但是由于没有文档可以参考,而且测试环境相对比较单一,因此,我并不能确保我提供的包和函数一定没有错误。如果想将这种方法应用的正式系统中,请首先做好备份工作。

言归正传,在上面的步骤中,使用DUMP_ORPHAN_KEYS过程保存了坏块中的索引键值,下面就通过这些保存的键值来进行数据的恢复。

先看一下ORPHAN_KEY_TABLE的表结构:

SQL> DESC ORPHAN_KEY_TABLE

名称 是否为空? 类型

-------------------------------------- -------- --------------

SCHEMA_NAME NOT NULL VARCHAR2(30)

INDEX_NAME NOT NULL VARCHAR2(30)

IPART_NAME VARCHAR2(30)

INDEX_ID NOT NULL NUMBER

TABLE_NAME NOT NULL VARCHAR2(30)

PART_NAME VARCHAR2(30)

TABLE_ID NOT NULL NUMBER

KEYROWID NOT NULL ROWID

KEY NOT NULL ROWID

DUMP_TIMESTAMP NOT NULL DATE

由于字段名基本上都是自解释的,这里就不再过多描述了,需要说明的是KEYROWID和KEY两个字段。

KEYROWID存放的是该索引键值对应的表中的ROWID,而KEY保存的就是索引的键值。

但是查询KEY的值发现,并非像想象中一样,存放的是1、2、3……或ALL_TABLES、ACCESS$……等值,而是以逻辑ROWID方式存放的。

SQL> SELECT KEY FROM ORPHAN_KEY_TABLE WHERE INDEX_NAME = ''IND_TEST_ID'' AND ROWNUM = 1;

KEY

---------------------------------------------------------------

*BAAAAAACwQL+

SQL> SELECT KEY FROM ORPHAN_KEY_TABLE WHERE INDEX_NAME = ''IND_TEST_NAME'' AND ROWNUM = 1;

KEY

---------------------------------------------------------------

*BAAAAAAHQUNDRVNTJP4

如何将逻辑ROWID转化为NUMBER或VARCHAR2类型呢?Oracle的文档并没有找到相应的解决方法。

于是抱着尝试的想法对这个字段DUMP一下。

SQL> SELECT DUMP(KEY) FROM ORPHAN_KEY_TABLE WHERE INDEX_NAME = ''IND_TEST_ID'' AND ROWNUM < 6;

DUMP(KEY)

----------------------------------------------------------------

Typ=208 Len=10: 2,4,0,0,0,0,2,193,2,254

Typ=208 Len=10: 2,4,0,0,0,0,2,193,3,254

Typ=208 Len=10: 2,4,0,0,0,0,2,193,4,254

Typ=208 Len=10: 2,4,0,0,0,0,2,193,5,254

Typ=208 Len=10: 2,4,0,0,0,0,2,193,6,254

这回看到希望了。还记得上面修改数据文件时123的编码吗?是不是和第一个查询中的结果很相似?

2,4,0,0,0,0前几位是不变的,最后一位254也是不变的。中间的部分就是有意义的数字了。其中第一个2表示长度是2,193表示最高位是个位,2表示最高位上的值是1,也就是说,第一个键值是数字1。(可以参考我对逻辑ROWID的存储格式进行分析的帖子)

SQL> SELECT DUMP(1) FROM DUAL;

DUMP(1)

------------------

Typ=2 Len=2: 193,2

Oracle把数据在文件中的存储格式保存在ROWID字段中了。根据这个假设,我们看看字符串是不是以同样方式存储的。

SQL> SELECT DUMP(KEY) FROM ORPHAN_KEY_TABLE WHERE INDEX_NAME = ''IND_TEST_NAME'' AND ROWNUM < 6;

DUMP(KEY)

----------------------------------------------------------------

Typ=208 Len=15: 2,4,0,0,0,0,7,65,67,67,69,83,83,36,254

Typ=208 Len=17: 2,4,0,0,0,0,9,65,71,71,88,77,76,73,77,80,254

Typ=208 Len=23: 2,4,0,0,0,0,15,65,71,71,88,77,76,73,78,80,85,84,84,89,80,69,254

Typ=208 Len=22: 2,4,0,0,0,0,14,65,76,76,95,65,76,76,95,84,65,66,76,69,83,254

Typ=208 Len=17: 2,4,0,0,0,0,9,65,76,76,95,65,80,80,76,89,254

显然7代表长度,后面跟着的明显就是ASCII编码。

SQL> SELECT CHR(65)||CHR(67)||CHR(67)||CHR(69)||CHR(83)||CHR(83)||CHR(36) FROM DUAL;

CHR(65)

-------

ACCESS$

知道这个规律,就可以着手进行恢复了。我写了一个包,用来恢复各种数据类型的数据。

SQL> CREATE OR REPLACE PACKAGE P_DUMPROWID

2 AS

3 --内部函数,由于去掉DUMP结果中的开头描述部分

4 FUNCTION F_GET_VALUE(P_STR IN VARCHAR2) RETURN VARCHAR2;

5 PRAGMA RESTRICT_REFERENCES (F_GET_VALUE, WNDS, WNPS, RNDS, RNPS);

6

7 --整理DUMP结果中16进制数,一位数的情况前面补0,并过滤逗号

8 FUNCTION F_ADD_PREFIX_ZERO (P_STR IN VARCHAR2, P_POSITION IN NUMBER) RETURN VARCHAR2;

9 PRAGMA RESTRICT_REFERENCES (F_ADD_PREFIX_ZERO, WNDS, WNPS, RNDS, RNPS);

10

11 --根据输入ROWID、方案名、索引名和索引中列的位置,将ROWID中的列的值提取出来

12 FUNCTION F_DUMP_FROM_ROWID

13 (

14 P_DUMP_ROWID IN UROWID,

15 P_OWNER IN VARCHAR2,

16 P_INDEX_NAME IN VARCHAR2,

17 P_COLUMN_POSITION IN NUMBER DEFAULT 1

18 )

19 RETURN VARCHAR2;

20 PRAGMA RESTRICT_REFERENCES (F_DUMP_FROM_ROWID, WNDS, WNPS);

21

22 END P_DUMPROWID;

23 /

程序包已创建。

SQL> CREATE OR REPLACE PACKAGE BODY P_DUMPROWID AS

2

3 FUNCTION F_GET_VALUE(P_STR IN VARCHAR2) RETURN VARCHAR2 AS

4 BEGIN

5 RETURN (SUBSTR(P_STR, INSTR(P_STR, '':'') + 2));

6 END F_GET_VALUE;

7

8 FUNCTION F_ADD_PREFIX_ZERO (P_STR IN VARCHAR2, P_POSITION IN NUMBER) RETURN VARCHAR2

9 AS

10 V_STR VARCHAR2(30000) := P_STR;

11 V_POSITION NUMBER := P_POSITION;

12 V_STR_PART VARCHAR2(2);

13 V_RETURN VARCHAR2(30000);

14 BEGIN

15 WHILE (V_POSITION != 0) LOOP

16 V_STR_PART := SUBSTR(V_STR, 1, V_POSITION - 1);

17 V_STR := SUBSTR(V_STR, V_POSITION + 1);

18

19 IF V_POSITION = 2 THEN

20 V_RETURN := V_RETURN || ''0'' || V_STR_PART;

21 ELSIF V_POSITION = 3 THEN

22 V_RETURN := V_RETURN || V_STR_PART;

23 ELSE

24 RAISE_APPLICATION_ERROR(-20002, ''DUMP ERROR CHECK THE INPUT ROWID'');

25 END IF;

26

27 V_POSITION := INSTR(V_STR, '','');

28 END LOOP;

29 RETURN REPLACE(V_RETURN , '','');

30 END F_ADD_PREFIX_ZERO;

31

32 FUNCTION F_DUMP_FROM_ROWID

33 (

34 P_DUMP_ROWID IN UROWID,

35 P_OWNER IN VARCHAR2,

36 P_INDEX_NAME IN VARCHAR2,

37 P_COLUMN_POSITION IN NUMBER DEFAULT 1

38 )

39 RETURN VARCHAR2 AS

40 V_COLUMN_TYPE DBA_TAB_COLUMNS.DATA_TYPE%TYPE;

41

42 V_LENGTH_STR VARCHAR2(10);

43 V_LENGTH NUMBER DEFAULT 7;

44 V_DUMP_ROWID VARCHAR2(30000);

45

46 V_DATE_STR VARCHAR2(100);

47 TYPE T_DATE IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;

48 V_DATE T_DATE;

49

50 BEGIN

51

52 --根据SCHEMA、INDEX_NAME和COLUMN_NAME得到COLUMN的数据类型

53 SELECT T.DATA_TYPE

54 INTO V_COLUMN_TYPE

55 FROM DBA_IND_COLUMNS I, DBA_TAB_COLUMNS T

56 WHERE I.TABLE_NAME = T.TABLE_NAME

57 AND I.TABLE_OWNER = T.OWNER

58 AND I.COLUMN_NAME = T.COLUMN_NAME

59 AND I.TABLE_OWNER = P_OWNER

60 AND I.INDEX_NAME = P_INDEX_NAME

61 AND I.COLUMN_POSITION = P_COLUMN_POSITION;

62

63 --根据COLUMN在索引中的位置,循环找到这个COLUMN对应的ROWID

64 FOR I IN 1..P_COLUMN_POSITION LOOP

65

66 --如果COLUMN的长度超过127,即表示长度的位超过7f,则开始用两位来存储长度,其中第一位以8开始。

67 SELECT F_GET_VALUE(DUMP(P_DUMP_ROWID, 16, V_LENGTH, 1)) INTO V_LENGTH_STR FROM DUAL;

68 IF SUBSTR(V_LENGTH_STR, 1, 1) = ''8'' THEN

69 SELECT SUBSTR(F_GET_VALUE(DUMP(P_DUMP_ROWID, 16, V_LENGTH, 2)), 2) INTO V_LENGTH_STR FROM DUAL;

70 V_LENGTH_STR := TO_CHAR(TO_NUMBER(REPLACE(V_LENGTH_STR, '',''), ''XXXX''));

71 SELECT F_GET_VALUE(DUMP(P_DUMP_ROWID, 16, V_LENGTH + 2, TO_NUMBER(V_LENGTH_STR))) INTO V_DUMP_ROWID

72 FROM DUAL;

73 V_LENGTH := V_LENGTH + TO_NUMBER(V_LENGTH_STR) + 2;

74 ELSE

75 V_LENGTH_STR := TO_CHAR(TO_NUMBER(V_LENGTH_STR, ''XXX''));

76 SELECT F_GET_VALUE(DUMP(P_DUMP_ROWID, 16, V_LENGTH + 1, TO_NUMBER(V_LENGTH_STR))) INTO V_DUMP_ROWID

77 FROM DUAL;

78 V_LENGTH := V_LENGTH + TO_NUMBER(V_LENGTH_STR) + 1;

79 END IF;

80 END LOOP;

81

82 IF V_COLUMN_TYPE = ''VARCHAR2'' OR V_COLUMN_TYPE = ''CHAR'' THEN

83

84 V_DUMP_ROWID :=F_ADD_PREFIX_ZERO(V_DUMP_ROWID || '','', INSTR(V_DUMP_ROWID, '',''));

85

86 RETURN(UTL_RAW.CAST_TO_VARCHAR2(V_DUMP_ROWID));

87 ELSIF V_COLUMN_TYPE = ''NUMBER'' THEN

88

89 V_DUMP_ROWID :=F_ADD_PREFIX_ZERO(V_DUMP_ROWID || '','', INSTR(V_DUMP_ROWID, '',''));

90

91 RETURN(TO_CHAR(UTL_RAW.CAST_TO_NUMBER(V_DUMP_ROWID)));

92 ELSIF V_COLUMN_TYPE = ''DATE'' THEN

93

94 V_DUMP_ROWID := '','' || V_DUMP_ROWID || '','';

95

96 FOR I IN 1..7 LOOP

97 V_DATE(I) := TO_NUMBER(SUBSTR(V_DUMP_ROWID, INSTR(V_DUMP_ROWID, '','', 1, I) + 1,

98 INSTR(V_DUMP_ROWID, '','', 1, I + 1) - INSTR(V_DUMP_ROWID, '','', 1, I) - 1), ''XXX'');

99 END LOOP;

100

101 V_DATE(1) := V_DATE(1) - 100;

102 V_DATE(2) := V_DATE(2) - 100;

103

104 IF ((V_DATE(1) < 0) OR (V_DATE(2) < 0)) THEN

105 V_DATE_STR := ''-'' || LTRIM(TO_CHAR(ABS(V_DATE(1)), ''00'')) || LTRIM(TO_CHAR(ABS(V_DATE(2)), ''00''));

106 ELSE

107 V_DATE_STR := LTRIM(TO_CHAR(ABS(V_DATE(1)), ''00'')) || LTRIM(TO_CHAR(ABS(V_DATE(2)),''00''));

108 END IF;

109

110 V_DATE_STR := V_DATE_STR || ''-'' || TO_CHAR(V_DATE(3)) || ''-'' || TO_CHAR(V_DATE(4)) || '' '' ||

111 TO_CHAR(V_DATE(5) - 1) || '':'' || TO_CHAR(V_DATE(6) - 1) || '':'' || TO_CHAR(V_DATE(7) - 1);

112 RETURN (V_DATE_STR);

113 ELSIF V_COLUMN_TYPE LIKE ''TIMESTAMP(_)'' THEN

114

115 V_DUMP_ROWID := '','' || V_DUMP_ROWID || '','';

116

117 FOR I IN 1..11 LOOP

118 V_DATE(I) := TO_NUMBER(SUBSTR(V_DUMP_ROWID, INSTR(V_DUMP_ROWID, '','', 1, I) + 1,

119 INSTR(V_DUMP_ROWID, '','', 1, I + 1) - INSTR(V_DUMP_ROWID, '','', 1, I) - 1), ''XXX'');

120 END LOOP;

121

122 V_DATE(1) := V_DATE(1) - 100;

123 V_DATE(2) := V_DATE(2) - 100;

124

125 IF ((V_DATE(1) < 0) OR (V_DATE(2) < 0)) THEN

126 V_DATE_STR := ''-'' || LTRIM(TO_CHAR(ABS(V_DATE(1)), ''00'')) || LTRIM(TO_CHAR(ABS(V_DATE(2)), ''00''));

127 ELSE

128 V_DATE_STR := LTRIM(TO_CHAR(ABS(V_DATE(1)), ''00'')) || LTRIM(TO_CHAR(ABS(V_DATE(2)),''00''));

129 END IF;

130

131 V_DATE_STR := V_DATE_STR || ''-'' || TO_CHAR(V_DATE(3)) || ''-'' || TO_CHAR(V_DATE(4)) || '' '' ||

132 TO_CHAR(V_DATE(5) - 1) || '':'' || TO_CHAR(V_DATE(6) - 1) || '':'' || TO_CHAR(V_DATE(7) - 1) || ''.'' ||

133 SUBSTR(TO_CHAR(V_DATE(8) * POWER(256, 3) + V_DATE(9) * POWER(256, 2) + V_DATE(10) * 256 + V_DATE(11)),

134 1, TO_NUMBER(SUBSTR(V_COLUMN_TYPE, 11, 1)));

135 RETURN (V_DATE_STR);

136 ELSIF V_COLUMN_TYPE = ''RAW'' THEN

137

138 V_DUMP_ROWID :=F_ADD_PREFIX_ZERO(V_DUMP_ROWID || '','', INSTR(V_DUMP_ROWID, '',''));

139

140 RETURN(V_DUMP_ROWID);

141 ELSIF V_COLUMN_TYPE = ''ROWID'' THEN

142

143 V_DUMP_ROWID :=F_ADD_PREFIX_ZERO(V_DUMP_ROWID || '','', INSTR(V_DUMP_ROWID, '',''));

144

145 RETURN (DBMS_ROWID.ROWID_CREATE(

146 1,

147 TO_NUMBER(SUBSTR(V_DUMP_ROWID, 1, 8), ''XXXXXXXXXXX''),

148 TRUNC(TO_NUMBER(SUBSTR(V_DUMP_ROWID, 9, 4), ''XXXXXX'')/64),

149 TO_NUMBER(MOD(TO_NUMBER(SUBSTR(V_DUMP_ROWID, 9, 4), ''XXXXXX''), 64) ||

150 TO_NUMBER(SUBSTR(V_DUMP_ROWID, 13, 4), ''XXXXXXXXXXX'')),

151 TO_NUMBER(SUBSTR(V_DUMP_ROWID, 17, 4), ''XXXXXX'')));

152 ELSE

153 RAISE_APPLICATION_ERROR(-20001, ''TYPE NOT VALID OR CAN''''T TRANSALTE '' || V_COLUMN_TYPE || '' TYPE'');

154 END IF;

155 EXCEPTION

156 WHEN NO_DATA_FOUND THEN

157 RAISE_APPLICATION_ERROR(-20003,

158 ''INVALID SCHEMA_NAME OR INVALID INDEX_NAME OR INVALID COLUMN_NAME OR INVALID COLUMN_POSITION'');

159 END F_DUMP_FROM_ROWID;

160

161 END P_DUMPROWID;

162 /

程序包主体已创建。

这个包主要对外提供一个函数F_DUMP_FROM_ROWID,另外两个函数是为了包中内部调用的。这个函数的输入参数分别是逻辑ROWID,表所在的SCHEMA,索引名称和抽取的列在索引中的位置。前三个参数分别由ORPHAN_KEY_TABLE表的KEY、SCHEMA_NAME和INDEX_NAME三个字段的值提供,最后一个参数的默认值为1,对于单列索引或复合索引的第一个字段的抽取,可以省略这个参数。

由于dbms_repair包一般是由DBA执行的,因此一般把这个包也建立在SYS帐户中,如果用普通用户建立,需要对DBA_TAB_COLUMNS和DBA_IND_COLUMNS的单独授权。下面测试一下函数的功能。

SQL> SELECT P_DUMPROWID.F_DUMP_FROM_ROWID(KEY, SCHEMA_NAME, INDEX_NAME) DUMP FROM ORPHAN_KEY_TABLE

2 WHERE INDEX_NAME = ''IND_TEST_ID'' AND ROWNUM < 6;

DUMP

-------------------------------------------

1

2

3

4

5

SQL> SELECT P_DUMPROWID.F_DUMP_FROM_ROWID(KEY, SCHEMA_NAME, INDEX_NAME, 1) DUMP FROM ORPHAN_KEY_TABLE

2 WHERE INDEX_NAME = ''IND_TEST_NAME'' AND ROWNUM < 6;

DUMP

-------------------------------------------

ACCESS$

AGGXMLIMP

AGGXMLINPUTTYPE

ALL_ALL_TABLES

ALL_APPLY

好了,剩下的事情就简单了。我们将ORPHAN_KEY_TABLE表中的记录转变后,重新插入到TEST表中即可。

SQL> INSERT INTO YANGTK.TEST (ID, NAME)

2 SELECT

3 P_DUMPROWID.F_DUMP_FROM_ROWID(A.KEY, A.SCHEMA_NAME, A.INDEX_NAME),

4 P_DUMPROWID.F_DUMP_FROM_ROWID(B.KEY, B.SCHEMA_NAME, B.INDEX_NAME)

5 FROM ORPHAN_KEY_TABLE A, ORPHAN_KEY_TABLE B

6 WHERE A.KEYROWID = B.KEYROWID

7 AND A.INDEX_NAME = ''IND_TEST_ID''

8 AND B.INDEX_NAME = ''IND_TEST_NAME'';

已创建549行。

SQL> SELECT * FROM YANGTK.TEST WHERE ID = 1;

ID NAME

---------- ------------------------------

1 ACCESS$

SQL> SELECT * FROM YANGTK.TEST WHERE ID = 123;

ID NAME

---------- ------------------------------

123 ALL_REPCONFLICT

SQL> COMMIT;

至此,已经成功的恢复了数据。

3 使用P_DUMPROWID恢复数据的例子

下面再简单说明一下这个程序包是如何恢复其他数据类型的。

通过查询ORPHAN_KEY_TABLE可以发现,Oracle把索引的值保存在逻辑ROWID中,我这个包实现的其实就是将逻辑ROWID中的值还原为索引键值。下面的例子为了简单起见,通过建立索引组织表,然后使用这个包还原索引组织表的ROWID信息作为参考的例子。

例1:对NUMBER类型、VARCHAR2类型和TIME类型的恢复,对复合索引的恢复。

SQL> create table test_index (id number, name varchar2(30), time date,

2 constraint pk_test_index primary key (id, name, time))

3 organization index;

表已创建。

SQL> insert into test_index select rownum, object_name, created from user_objects;

已创建64行。

SQL> commit;

提交完成。

SQL> col dump format a30

SQL> alter session set nls_date_format = ''yyyy-mm-dd hh24:mi:ss'';

会话已更改。

SQL> select id, p_dumprowid.f_dump_from_rowid(ROWID, ''YANGTK'', ''PK_TEST_INDEX'') dump

2 from test_index where rownum < 5;

ID DUMP

---------- ------------------------------

1 1

2 2

3 3

4 4

SQL> select name, p_dumprowid.f_dump_from_rowid(ROWID, ''YANGTK'', ''PK_TEST_INDEX'', 2) dump

2 from test_index where rownum < 5;

NAME DUMP

------------------------------ ------------------------------

AA AA

IND_Q1_ID IND_Q1_ID

IND_Q1_ID IND_Q1_ID

IND_Q1_ID IND_Q1_ID

SQL> select time, p_dumprowid.f_dump_from_rowid(ROWID, ''YANGTK'', ''PK_TEST_INDEX'', 3) dump

2 from test_index where rownum < 5;

TIME DUMP

------------------- ------------------------------

2004-12-19 02:36:33 2004-12-19 2:36:33

2004-12-18 23:17:56 2004-12-18 23:17:56

2004-12-18 23:17:56 2004-12-18 23:17:56

2004-12-18 23:18:33 2004-12-18 23:18:33

例二:对TIMESTAMP类型的恢复。

SQL> create table test_stamp (time timestamp constraint pk_test_stamp primary key)

2 organization index;

表已创建。

SQL> insert into test_stamp values (systimestamp);

已创建 1 行。

SQL> alter session set nls_timestamp_format = ''yyyy-mm-dd hh24:mi:ss.ff'';

会话已更改。

SQL> col dump format a40

SQL> col time format a40

SQL> select time, p_dumprowid.f_dump_from_rowid(rowid, ''YANGTK'', ''PK_TEST_STAMP'') dump

2 from test_stamp;

TIME DUMP

---------------------------------------- ----------------------------------------

2005-01-13 00:44:27.392000 2005-1-13 0:44:27.392000

SQL> alter table test_stamp modify (time timestamp(9));

表已更改。

SQL> insert into test_stamp values (to_timestamp(''2005-1-1 12:23:23.334282122'',

2 ''yyyy-mm-dd hh24:mi:ss.ff''));

已创建 1 行。

SQL> select time, p_dumprowid.f_dump_from_rowid(rowid, ''YANGTK'', ''PK_TEST_STAMP'') dump

2 from test_stamp;

TIME DUMP

---------------------------------------- ----------------------------------------

2005-01-01 12:23:23.334282122 2005-1-1 12:23:23.334282122

2005-01-13 00:44:27.392000000 2005-1-13 0:44:27.392000000

这个包可以支持普通的单列BTREE索引和复合BTREE索引,不支持reverse key索引和BITMAP索引。

目前支持的数据类型包括CHAR、VARCHAR2、NUMBER、DATE、TIMESTAMP、RAW和物理ROWID类型。这个包不支持的类型包括NCHAR、NVARCHAR2、TIMESTAMP WITH LOCAL TIME ZONE和TIMESTAMP WITH TIME ZONE等几种不很常用的类型(LONG、LONG RAW、逻辑ROWID和LOB类型不支持索引)。

如果VARCHAR2(CHAR)类型中包含中文,在ZHS16GBK字符集下我测试通过,其他字符集没有测试,但估计对于一般中文字符集都不会有问题,但是如果用单字节字符集表示中文可能会有问题。

任何一个工具也不可能只有好的方面而没有任何缺点。使用DBMS_REPAIR包的同时会带来数据丢失、表和索引返回数据不一致,完整性约束破坏等问题。因此当出现错误时,首先应当考虑用物理备份或逻辑备份进行恢复,DBMS_REPAIR包应该只是在没有备份的情况下使用的一种手段。DMBS_REPAIR包无法恢复表中没有被索引的列,因此使用这种方式一般都会造成数据的丢失。

参考文档:

1.Oracle9i Administrator’s Guide

2.Oracle9i Database Concepts

3.Supplied PL/SQL Packages and Types Reference

我在ITPUB上的这篇帖子包括了对Oracle基本数据类型的描述:

http://www.itpub.net/308317.html

0
相关文章