oracle dbms_repair,DBMS_REPAIR的使用_CalvinZXJ的博客-程序员秘密

技术标签: oracle dbms_repair  

Oracle提供了DBMS_REPAIR包用来发现、标识并修改数据文件中的坏块。

任何工具都不是万能的,使用这个包的同时会带来数据丢失、表和索引返回数据不一致,完整性约束破坏等其他问题。因此当出现错误时,应当首先从物理备份或逻辑备份恢复,使用dbms_repair只是在没有备份的情况下使用的一种手段,这种方式一般都会造成数据的丢失。

dbms_repair包的工作原理比较简单,是将检查到的坏块标注出来,使随后的dml操作跳过该块,同时,dbms_repair包还提供了用于保存索引中包含的标注为坏块中的键值,以及修复freelist和segment bitmap的过程。

有一点需要注意,dbms_repair包没有进行授权,只有sys用户可以执行。

下面通过一个例子来简要介绍一下dbms_repair包的使用。

一、构造测试环境

首先建立一个测试用表空间,由于需要用UtralEdit打开数据文件修改部分内容来模拟错误,因此数据文件要建的小一些。

SQL> CREATE TABLESPACE TEST DATAFILE 'E:ORACLEORADATATESTTEST.DBF' SIZE 1M

2  EXTENT MANAGEMENT LOCAL AUTOALLOCATE SEGMENT SPACE MANAGEMENT MANUAL;

表空间已创建。

SQL> CREATE TABLE TEST (ID NUMBER, NAME VARCHAR2(30)) TABLESPACE TEST;

表已创建。

SQL> INSERT INTO TEST SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;

已创建6232行。

SQL> COMMIT;

提交完成。

SQL> CREATE INDEX IND_TEST_ID ON TEST (ID);

索引已创建。

SQL> CREATE INDEX IND_TEST_NAME ON TEST (NAME);

索引已创建。

为了确保oracle已经把刚才插入的数据写到数据文件中,现在重起数据库。

SQL> CONN /@TEST AS SYSDBA

已连接。

SQL> SHUTDOWN

数据库已经关闭。

已经卸载数据库。

ORACLE 例程已经关闭。

SQL> STARTUP

ORACLE 例程已经启动。

Total System Global Area   89201304 bytes

Fixed Size                   453272 bytes

Variable Size              62914560 bytes

Database Buffers           25165824 bytes

Redo Buffers                 667648 bytes

数据库装载完毕。

数据库已经打开。

二、模拟错误的产生

用UtralEdit打开数据文件,只要修改了数据文件中任意的一个位置,都会造成数据文件错误。但我们测试需要将错误发生位置定位在TEST表中。

SQL> CONN YANGTK/[email protected]

已连接。

SQL> SELECT SUBSTR(ROWID, 10, 6), ID, NAME FROM TEST WHERE ID = 123;

SUBSTR(ROWID         ID NAME

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

AAAAAG              123 ALL_REPCONFLICT

如何在数据文件中找到TEST表的数据呢?可以通过ROWID来定位的记录在数据文件中的位置。任意选择一条记录(如上面ID = 123),取得它的ROWID,我们知道,ROWID中10~15位表示这条记录所在的BLOCK是数据文件的第几个BLOCK。

A表示0,B为1,G表示6。这说明这条记录在数据文件的第六个block中。

SQL> SHOW PARAMETER DB_BLOCK_SIZE

NAME                                 TYPE        VALUE

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

db_block_size                        integer     16384

BLOCK的大小是16k。

SQL> SELECT TO_CHAR(6*16384, 'XXXXXX') FROM DUAL;

TO_CHAR

-------

18000

SQL> SELECT TO_CHAR(7*16384, 'XXXXXX') FROM DUAL;

TO_CHAR

-------

1C000

用UtralEdit打开数据文件,将文件定位18000h处(以二进制方式打开,如果没有用二进制打开,可以使用CTRL+H快捷键切换)。根据上面的计算,可以得出,我们要找到记录在18000h和1C000h之间。

Number类型123在数据库存放方式为03C20218,03表示占有三位,C2表示最高位是百位,02表示最高位上是1,18表示低位上是23。

具体的数值可以通过下面的查询得到:

SQL> SELECT DUMP(123) FROM DUAL;

DUMP(123)

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

Typ=2 Len=3: 194,2,24

SQL> SELECT TO_CHAR(194, 'XX'), TO_CHAR(2, 'XX'), TO_CHAR(24, 'XX') FROM DUAL;

TO_ TO_ TO_

--- --- ---

C2   2  18

关于具体的NUMBER类型在数据库中是如何存储的,有兴趣的可以参阅另一篇文章。

下面使用UtralEdit的搜索功能,查找到03C20218,将其修改为03C20216,并保存。

上面是通过oracle的ROWID在文件中定位,这相对来说要复杂一些。下面可以使用UtralEdit的功能达到相同的目的。

根据上面的查询可以得到,ID = 123时,NAME的值是ALL_REPCONFLICT。

下面用UtralEdit打开文件,使用CTRL+H方式切换到文本格式,直接查找ALL_REPCONFLICT字符串。找到后,CTRL+H切换回二进制格式。向前跳过一个长度字节(本例中为0F),就可以看到123的值03C20218,进行修改后,保存并退出。

SQL> SELECT * FROM TEST WHERE ID = 123;

ID NAME

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

123 ALL_REPCONFLICT

这时候查询仍然可以得到正确结果,因为oracle使用了db_cache中的结果。为了让oracle“看”到修改,必须重起数据库。

SQL> CONN /@TEST AS SYSDBA

已连接。

SQL> SHUTDOWN

数据库已经关闭。

已经卸载数据库。

ORACLE 例程已经关闭。

SQL> STARTUP

ORACLE 例程已经启动。

Total System Global Area   89201304 bytes

Fixed Size                   453272 bytes

Variable Size              62914560 bytes

Database Buffers           25165824 bytes

Redo Buffers                 667648 bytes

数据库装载完毕。

数据库已经打开。

SQL> CONN YANGTK/[email protected]

已连接。

SQL> SELECT * FROM TEST WHERE ID = 123;

SELECT * FROM TEST WHERE ID = 123

*

ERROR 位于第 1 行:

ORA-01578: ORACLE 数据块损坏(文件号7,块号6)

ORA-01110: 数据文件 7: 'E:ORACLEORADATATESTTEST.DBF'

已经模拟成功了坏块,开始进入正题部分,使用DBMS_REPAIR表来处理坏块。

三、使用DBMS_REPAIR包处理坏块。

1.建立REPAIR_TABLE和ORPHAN_KEY_TABLE表

SQL> BEGIN

2  DBMS_REPAIR.ADMIN_TABLES (

3  TABLE_NAME => 'REPAIR_TABLE',

4  TABLE_TYPE => dbms_repair.repair_table,

5  ACTION => dbms_repair.create_action,

6  TABLESPACE => 'YANGTK');

7  END;

8  /

PL/SQL 过程已成功完成。

SQL> BEGIN

2  DBMS_REPAIR.ADMIN_TABLES (

3  TABLE_NAME => 'ORPHAN_KEY_TABLE',

4  TABLE_TYPE => dbms_repair.orphan_table,

5  ACTION => dbms_repair.create_action,

6  TABLESPACE => 'YANGTK');

7  END;

8  /

PL/SQL 过程已成功完成。

REPAIR_TABLE用来记录错误检查结果,ORPHAN_KEY_TABLE用来记录表坏块中记录在索引中对应键值。

这两个表的删除可以通过下列存储过程完成

BEGIN

DBMS_REPAIR.ADMIN_TABLES (

TABLE_NAME => 'REPAIR_TABLE',

TABLE_TYPE => dbms_repair.repair_table,

ACTION => dbms_repair.drop_action);

END;

/

BEGIN

DBMS_REPAIR.ADMIN_TABLES (

TABLE_NAME => 'ORPHAN_KEY_TABLE',

TABLE_TYPE => dbms_repair.orphan_table,

ACTION => dbms_repair.drop_action);

END;

/

2.使用CHECK_OBJECT过程检测坏块。

SQL> SET SERVEROUTPUT ON

SQL> DECLARE

2  num_corrupt INT;

3  BEGIN

4  num_corrupt := 0;

5  DBMS_REPAIR.CHECK_OBJECT (

6  SCHEMA_NAME => 'YANGTK',

7  OBJECT_NAME => 'TEST',

8  REPAIR_TABLE_NAME => 'REPAIR_TABLE',

9  CORRUPT_COUNT => num_corrupt);

10  DBMS_OUTPUT.PUT_LINE('number corrupt: ' || TO_CHAR (num_corrupt));

11  END;

12  /

number corrupt: 1

PL/SQL 过程已成功完成。

SQL> SELECT OBJECT_NAME, BLOCK_ID, CORRUPT_TYPE, MARKED_CORRUPT,

2  CORRUPT_DESCRIPTION, REPAIR_DESCRIPTION

3  FROM REPAIR_TABLE;

OBJECT_NAM   BLOCK_ID CORRUPT_TYPE MARKED_COR CORRUPT_DE REPAIR_DESCRIPTION

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

TEST                6         6148 TRUE                  mark block software corrupt

这里和oracle文档上面有点出入,根据oracle文档上面介绍MARKED_CORRUPT列的值是FALSE,只有执行了FIX_CORRUPT_BLOCKS过程才会使MARKED_CORRUPT列的值变为TRUE。怀疑oracle在CHECK的同时,自动进行FIX_CORRUPT_BLOCKS的操作。

SQL> DECLARE

2  num_fix INT;

3  BEGIN

4  num_fix := 0;

5  DBMS_REPAIR.FIX_CORRUPT_BLOCKS (

6  SCHEMA_NAME => 'YANGTK',

7  OBJECT_NAME=> 'TEST',

8  OBJECT_TYPE => dbms_repair.table_object,

9  REPAIR_TABLE_NAME => 'REPAIR_TABLE',

10  FIX_COUNT=> num_fix);

11  DBMS_OUTPUT.PUT_LINE('num fix: ' || TO_CHAR(num_fix));

12  END;

13  /

num fix: 0

PL/SQL 过程已成功完成。

果然,执行FIX_CORRUPT_BLOCKS过程发现FIX了0个坏块,这一步操作可以省略不用执行。

3.使用DUMP_ORPHAN_KEYS过程来保存坏块中的索引键值。

这时还存在着一个潜在的问题。表出现了坏块,但是索引没有损坏,通过表扫描回出现错误,但是通过索引扫描,仍然可以返回结果,这会造成数据的不一致性。

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

SELECT * FROM YANGTK.TEST WHERE ID = 123

*

ERROR 位于第 1 行:

ORA-01578: ORACLE 数据块损坏(文件号7,块号6)

ORA-01110: 数据文件 7: 'E:ORACLEORADATATESTTEST.DBF'

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

ID

----------

123

通过使用DUMP_ORPHAN_KEYS过程来保存坏块中的索引键值,这样当执行完SKIP_CORRUPT_BLOCKS操作后,就可以重新建立索引了。

SQL> DECLARE

2  num_orphans INT;

3  BEGIN

4  num_orphans := 0;

5  DBMS_REPAIR.DUMP_ORPHAN_KEYS (

6  SCHEMA_NAME => 'YANGTK',

7  OBJECT_NAME => 'IND_TEST_ID',

8  OBJECT_TYPE => dbms_repair.index_object,

9  REPAIR_TABLE_NAME => 'REPAIR_TABLE',

10  ORPHAN_TABLE_NAME=> 'ORPHAN_KEY_TABLE',

11  KEY_COUNT => num_orphans);

12  DBMS_OUTPUT.PUT_LINE('orphan key count: ' || TO_CHAR(num_orphans));

13  END;

14  /

orphan key count: 549

PL/SQL 过程已成功完成。

SQL> DECLARE

2  num_orphans INT;

3  BEGIN

4  num_orphans := 0;

5  DBMS_REPAIR.DUMP_ORPHAN_KEYS (

6  SCHEMA_NAME => 'YANGTK',

7  OBJECT_NAME => 'IND_TEST_NAME',

8  OBJECT_TYPE => dbms_repair.index_object,

9  REPAIR_TABLE_NAME => 'REPAIR_TABLE',

10  ORPHAN_TABLE_NAME=> 'ORPHAN_KEY_TABLE',

11  KEY_COUNT => num_orphans);

12  DBMS_OUTPUT.PUT_LINE('orphan key count: ' || TO_CHAR(num_orphans));

13  END;

14  /

orphan key count: 549

PL/SQL 过程已成功完成。

注意对每个索引都要执行DUMP_ORPHAN_KEYS过程。

4.使用REBUILD_FREELISTS过程来修改FREELISTS。

如果坏块发生在FREELIST列表中的中部,则FREELIST列表后面的块都无法访问,在这个例子中,由于是人为产生的错误,清楚错误的位置不在FREELIST中,因此可以跳过此步骤,一般情况下,无法定位坏块位置,则需要执行改过程。

SQL> BEGIN

2  DBMS_REPAIR.REBUILD_FREELISTS (

3  SCHEMA_NAME => 'YANGTK',

4  OBJECT_NAME => 'TEST',

5  OBJECT_TYPE => dbms_repair.table_object);

6  END;

7  /

PL/SQL 过程已成功完成。

5.执行SKIP_CORRUPT_BLOCKS过程,是后续DML操作跳过坏块

SQL> BEGIN

2  DBMS_REPAIR.SKIP_CORRUPT_BLOCKS (

3  SCHEMA_NAME => 'YANGTK',

4  OBJECT_NAME => 'TEST',

5  OBJECT_TYPE => dbms_repair.table_object,

6  FLAGS => dbms_repair.skip_flag);

7  END;

8  /

PL/SQL 过程已成功完成。

SQL> SELECT OWNER, TABLE_NAME, SKIP_CORRUPT FROM DBA_TABLES

2  WHERE OWNER = 'YANGTK';

OWNER                        TABLE_NAME                   SKIP_COR

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

YANGTK                       TEST                         ENABLED

YANGTK                       TEST1                        DISABLED

YANGTK                       TEST_AAA                     DISABLED

YANGTK                       TEST_PART                    DISABLED

已选择4行。

6.重建索引

由于数据和索引仍然存在不一致的问题,因此必须重建索引。

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

未选定行

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

ID

----------

123

SQL> ALTER INDEX YANGTK.IND_TEST_ID REBUILD;

索引已更改。

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

ID

----------

123

注意一点,重建索引一点要先DROP,然后再CREATE,使用REBUILD的方式,重建的数据源来自索引,仍然会导致问题的产生。

SQL> DROP INDEX YANGTK.IND_TEST_ID;

索引已丢弃。

SQL> DROP INDEX YANGTK.IND_TEST_NAME;

索引已丢弃。

SQL> CREATE INDEX YANGTK.IND_TEST_ID ON YANGTK.TEST(ID);

索引已创建。

SQL> CREATE INDEX YANGTK.IND_TEST_NAME ON YANGTK.TEST(NAME);

索引已创建。

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

未选定行

SQL> SELECT MIN(ID) FROM YANGTK.TEST;

MIN(ID)

----------

550

包含ID = 123的块已经别标识为坏块。现在可以看到,最小的ID是550,也就是说,这个坏块中包含了549条记录。

SQL> SELECT COUNT(*) FROM ORPHAN_KEY_TABLE;

COUNT(*)

----------

1098

继续查询ORPHAN_KEY_TABLE表,可以发现,这些记录的索引(2个)已经被保存到了ORPHAN_KEY_TABLE表中。

四、恢复数据

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

由于坏块产生在表上,因此索引是可以访问,所有被索引的列的数据都可以恢复。遗憾的是,Oracle的文档并没有给出恢复的方法,我查询了METALINK和ASKTOM也没有找到相应的答案,所以,恢复的工作只能靠自己摸索进行。因此,我并不能保证我的方法一定是正确的,如果想将这种方法应用的正式环境中,请慎重考虑,本人不承担任何责任。:)

言归正传,在上面的步骤中,使用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类型呢?查询文档并没有找到相应的解决方法。

于是抱着尝试的想法,觉得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。

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$

知道这个规律,就可以着手进行恢复了,首先先了一个小程序用来把ROWID类型转换为NUMBER类型或VARCHAR2类型。

SQL> CREATE OR REPLACE FUNCTION F_DUMP_FROM_ROWID

2  (

3   P_TYPE IN VARCHAR2,

4   P_DUMP_ROWID IN UROWID

5  )

6  RETURN VARCHAR2 AS

7   V_DUMP_ROWID VARCHAR2(1000);

8   V_NUMBER_STR VARCHAR2(1000);

9   V_POSITION NUMBER;

10   V_NUMBER VARCHAR2(2);

11  BEGIN

12   SELECT DUMP(P_DUMP_ROWID, 16, 7) INTO V_DUMP_ROWID FROM DUAL;

13   V_DUMP_ROWID := SUBSTR(V_DUMP_ROWID, INSTR(V_DUMP_ROWID, ',') + 1);

14   V_DUMP_ROWID := SUBSTR(V_DUMP_ROWID, 1, LENGTH(V_DUMP_ROWID) - 3);

15

16   IF P_TYPE = 'VARCHAR2' THEN

17    V_DUMP_ROWID := REPLACE(V_DUMP_ROWID , ',');

18    RETURN(UTL_RAW.CAST_TO_VARCHAR2(V_DUMP_ROWID));

19   ELSIF P_TYPE = 'NUMBER' THEN

20    V_DUMP_ROWID := V_DUMP_ROWID || ',';

21    V_POSITION := INSTR(V_DUMP_ROWID, ',');

22

23    WHILE (V_POSITION != 0) LOOP

24     V_NUMBER := SUBSTR(V_DUMP_ROWID, 1, V_POSITION - 1);

25     V_DUMP_ROWID := SUBSTR(V_DUMP_ROWID, V_POSITION + 1);

26

27     IF V_POSITION = 2 THEN

28      V_NUMBER_STR := V_NUMBER_STR || '0' || V_NUMBER;

29     ELSIF V_POSITION = 3 THEN

30      V_NUMBER_STR := V_NUMBER_STR || V_NUMBER;

31     ELSE

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

33     END IF;

34

35     V_POSITION := INSTR(V_DUMP_ROWID, ',');

36    END LOOP;

37

38    V_NUMBER_STR := REPLACE(V_NUMBER_STR , ',');

39

40    RETURN(UTL_RAW.CAST_TO_NUMBER(V_NUMBER_STR));

41   ELSE

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

43   END IF;

44  END;

45  /

函数已创建。

测试一下函数的功能。

SQL> SELECT F_DUMP_FROM_ROWID('NUMBER', KEY) FROM ORPHAN_KEY_TABLE

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

F_DUMP_FROM_ROWID('NUMBER',KEY)

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

1

2

3

4

5

SQL> SELECT F_DUMP_FROM_ROWID('VARCHAR2', KEY) FROM ORPHAN_KEY_TABLE

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

F_DUMP_FROM_ROWID('VARCHAR2',KEY)

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

ACCESS$

AGGXMLIMP

AGGXMLINPUTTYPE

ALL_ALL_TABLES

ALL_APPLY

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

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

2  SELECT

3  F_DUMP_FROM_ROWID('NUMBER', A.KEY),

4  F_DUMP_FROM_ROWID('VARCHAR2', B.KEY)

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;

至此,数据恢复已经完成。

最后,需要说明几点。

我只做了NUMBER类型和VARCHAR2类型的恢复测试,其他类型的恢复还没有测试,目前我做的函数也只包含了恢复NUMBER和VARCHAR2类型的功能。而且,目前只进行了单列B数索引的恢复,组合索引、BITMAP索引等其它类型索引没有测试。另外,如果VARCHAR2(CHAR)类型中包含中文,在ZHS16GBK字符集下我测试通过,其他字符集没有测试,但估计对于一般中文字符集都不会有问题,但是单字节字符集可能会有问题。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33694136/article/details/116367341

智能推荐

windows下的tensorflow安装步骤(CPU版本)_tensorflow cpu版怎么安装_家在北方的博客-程序员秘密

1、下载安装python3.5 或 Anaconda3.5目前windows下只支持3.5版本,我这里安装Anaconda3.52、找到这个位置,将其发送到桌面快捷方式3、以管理员身份打开桌面的快捷方式,然后在网页里面自动打开了编译界面4、5、在里面编译,按shift+enter运行6、安装tensorflow(CPU版本),以管理员身份7、安装完成8、打开jupyter,输入import ten...

SDE数据在ArcMap下平移报错超出边界范围_kone666的博客-程序员秘密

错误信息:用ArcMap加载sde里的数据(数据空间参考是unkown),平移出当前视图窗口的时候报错:One or more layers failed to draw: The specified coordinate exceeds the valid coordinate range原因: domain设置的问题,数据在导入sde的时候默认是按照空间参考的dom

PHP程序员的技术成长规划 作者:黑夜路人(2014/10/15)_黑夜路人 程序员成长_代官山的博客-程序员秘密

PHP程序员的技术成长规划作者:黑夜路人(2014/10/15)  按照了解的很多PHP/LNMP程序员的发展轨迹,结合个人经验体会,抽象出很多程序员对未来的迷漫,特别对技术学习的盲目和慌乱,简单梳理了这个每个阶段PHP程序员的技术要求,来帮助很多PHP程序做对照设定学习成长目标。 本文按照目前主流技术做了一个基本的梳理,整个是假设PHP程序员不

[RK3288][Android6.0] 调试笔记 --- WiFi一直处于“正在连接”状态_rk3288 wifi 就死机_KrisFei的博客-程序员秘密

Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92现象:Wifi提示正在连接,但是一直连接不上。换了其他的一个AP,也提示连接不上。分析:根据WIFI连接过程可知,显示正在连接时dhcp client会从路由器那边获取ip address.正常Log: 06-13 14:56:23.697 583 13...

机器学习的模型评估(使用sklearn工具)_sklearn模型评估_田田天天甜甜的博客-程序员秘密

使用ROC曲线评估分类模型是非常通用的手段,但是,使用它的时候要注意两点:1、分类的类型。必须为数值型。2、只针对二分类问题。ROC曲线是根据一系列不同的二分类方式(分界值或决定阈),以真阳性率(灵敏度)为纵坐标,假阳性率(1-特异度)为横坐标绘制的曲线。传统的诊断试验评价方法有一个共同的特点,必须将试验结果分为两类,再进行统计分析。3.若预测多分类,解决方法是,把scorer=make...

华为s5700交换机使用配置_花红羊蹄甲的博客-程序员秘密

文章目录说明结论同网段直接接入不同网段直接接入说明这次要做的是不做任何配置,将设备接到交换机上,测试会发生什么交换机默认在配置模式下输入命令(即登录设备后执行system-view命令)结论如果华为s5700交换机没做任何配置No说明1将同网段设备接入此设备是互通的2将不同网段设备接入此设备是不通的同网段直接接入在A和B接上SW之前,我们......

随便推点

面试官:说说RabbitMQ 消费端限流、TTL、死信队列_公众号:方志朋的博客-程序员秘密

欢迎关注方志朋的博客,回复”666“获面试宝典1. 为什么要对消费端限流假设一个场景,首先,我们 Rabbitmq 服务器积压了有上万条未处理的消息,我们随便打开一个消费者客户端,会出现这...

LeetCode:面试题 16.17. Contiguous Sequence LCCI 连续数列(C语言)_wangqingchuan92的博客-程序员秘密

题目描述:给定一个整数数组,找出总和最大的连续数列,并返回总和。示例:输入: [-2,1,-3,4,-1,2,1,-5,4]输出: 6解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/contiguous-sequence-lcci著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

微信搜索功能测试点_微信搜索测试用例_懒羊羊跳跳的博客-程序员秘密

功能测试:搜索内容为空、空格、关键字中间有空格在允许的字数范围内搜索、 字数范围外搜索,是否截取或其它正确处理;输入特殊字符,表情符号,url链接输入框复制和粘贴功能取消搜索:未输入取消,已输入内容取消输入html脚本,转义字符,sql注入脚本,是否能正确处理输入敏感词是否有提示多次搜索同样的内容指定内容:搜索朋友圈、公众号、音乐、表情,结果验证语音:语音输入是否成功、语音输入...

uniapp H5端显示favicon.ico_sunly_的博客-程序员秘密

uniapp自定义template.h5.html下边这个例子来自官网的自定义模板。在根目录添加template.h5.html文件&lt;!DOCTYPE html&gt;&lt;html lang="zh-CN"&gt; &lt;head&gt; &lt;meta charset="utf-8"&gt; &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt; &lt;lin

UVa232 - Crossword Answers_fate94928的博客-程序员秘密

A crossword puzzle consists of a rectangular grid of black andwhite squares and two lists of definitions (or descriptions).One list of definitions is for “words” to be written left toright across whit...

推荐文章

热门文章

相关标签