不小心Truncate表的事情也是有的, 其中大部份时因为工具连错了库, 从儿跑错了角本. 遇到这种事情而没有备份时怎么办呢? 首先要停止数据库, 将这个表所在的表空间的文件拷贝出来, 因为OracleTruncate只时将相应Segment的第一个块格式化掉了, 而后面的都还存在, 到下次用时到才真正地重新格式化. 下面来讲一个Truncate表后进行恢复的例子: SQL> CREATE TABLE T_TRUNCATE AS SELECT * FROM TAB;

Table created.

SQL> SELECT COUNT(*) FROM T_TRUNCATE;

  COUNT(*)
----------
        14

SQL> ALTER SYSTEM CHECKPOINT;

System altered.

SQL> TRUNCATE TABLE T_TRUNCATE;

Table truncated.

SQL> ALTER SYSTEM CHECKPOINT;

System altered.
   
Truncate时只是Segment Header格式化了, 并将Data Object ID换成一个新的值, 我们可以在AUL中用DESC命令来查看:
AUL> desc anysql.t_truncate

Storage(OBJ#=9976 OBJD=9977 TS=4 FILE=4 BLOCK=5235 CLUSTER=0)
No. SEQ INT Column Name                   Type
--- --- --- ----------------------------- ----------------
  1   1   1 TNAME                         VARCHAR2(30) NOT NULL
  2   2   2 TABTYPE                       VARCHAR2(7)
  3   3   3 CLUSTERID                     NUMBER
   
要恢复这个表的数据, 首先要在AUL中运行SCAN EXTENT命令, 因为Segment Header被格式化了, 所以Extent Map也可能丢失, Scan Extent则将扫描整个数据文件并将Extent分配信息写入AULEXT.TXT文件:
AUL> SCAN EXTENT FILE 4
2006-12-18 21:32:10
2006-12-18 21:32:24
   
恢复的关键是要获得这个表原来的Data Object ID, 在这个例子中我在Truncate表后什么也没有做就关闭数据库进行恢复了. 从上面的DESC命令可以看出表的Segment Header(4,5235), 而新的Data Object ID9977, 老的Data Object ID我们可以从Segment Header的后面一个数据块中得到, 如果这个表有几个Free List Group, 则可能还要再后面几个块. AULORADUMP命令来看一下后面一个块:
AUL> ORADUMP FILE 4 BLOCK 5236
RDBA=0x01001474(4/5236)=16782452,type=0x06,fmt=0xa2,seq=0x02,flag=0x04
seg/obj=0x000026f8=9976,csc=0x0000.0006caf5,itc=3,typ=1 - DATA
FLG=0x32, fls=0, nxt=0x01001471(4/5233)=16782449
......
   
可以看到原来的Data Object ID9976, 现在可以恢复了, 先不指定原来的Data Object ID试试?
AUL> unload table anysql.t_truncate;
2006-12-18 21:33:37
Unload OBJD=9977 FILE=4 BLOCK=5235 CLUSTER=0 ...
2006-12-18 21:33:37

   
接下来指定原来的Data Object ID, 再试试?
AUL> unload table anysql.t_truncate object 9976;
2006-12-18 21:33:45
Unload OBJD=9976 FILE=4 BLOCK=5235 CLUSTER=0 ...
P_MV_FACT_SALES|TABLE
TIME_DIM|TABLE
FACT_SALES|TABLE
MV_FACT_SALES|TABLE
SEG$|TABLE
NUMTEST|TABLE
T_OBJECTS|TABLE
T_LOBTEST|TABLE
T_INCLOB|TABLE
CF_XXK|TABLE
T_TESTDMP|TABLE
T_CLOBDEMO|TABLE
T_BLOBDEMO|TABLE
T_TRUNCATE|TABLE
2006-12-18 21:33:45
   
可以看到14条数据全回来了, 当然数据库是复杂的, 如果是一个很大的表, 还是不能保证可以100%恢复的.

最近至少看到二次错误地截断(Truncate)表的例子, 并在网上询问如何恢复, 在这儿我给出AUL/MyDUL的解决方案, 下面是我用的一个测试表:
ASQL> DESC TRUNCDEMO

NO# NAME              NULLABLE TYPE
--- ----------------- -------- ------------
  1 COL1                       VARCHAR2(20)

ASQL> SELECT * FROM TRUNCDEMO;

COL1
-----
ROW 1
ROW 2

2 rows returned.

   
接下来我们来截断表, 其实这个操作只是重新格式化了段头块(Segment Header), 并分配一个新的数据对象号(Data Object ID), 当然空间分配信息也改了, 除非加了重用空间选项(Reuse Storage). 来看一下这个操作的前后变化:
ASQL> SELECT DATA_OBJECT_ID, OBJECT_NAME FROM USER_OBJECTS;

DATA_OBJECT_ID OBJECT_NAME
-------------- -----------
         13676 TRUNCDEMO

1 rows returned.

ASQL> truncate table truncdemo;

Truncate Table Succeed.

ASQL> SELECT DATA_OBJECT_ID, OBJECT_NAME FROM USER_OBJECTS;

DATA_OBJECT_ID OBJECT_NAME
-------------- -----------
         13677 TRUNCDEMO

1 rows returned.
   
由于在System表空间中已经记录了新的信息, 因此用当前的System信息是不能恢复过来的,AUL/MyDUL中可以当作没有System时的情况来处理,在下面的命令中, 我们用Truncate后的数据对象号就不能进行恢复, 而使用Truncate以前的就可以, 当然空间不能被重新利用了是恢复的前提.
AUL> unload object 13676 column varchar file 4;
2006-09-18 22:38:58
ROW 1
ROW 2
2006-09-18 22:39:04
AUL> unload object 13677 column varchar file 4;
2006-09-18 22:39:10
2006-09-18 22:39:10
AUL>
   
因此在意外发生Truncate, 如果没有备份可以恢复, 首先要做的事是备份一下当前的文件, 免得空间被重用. Truncate之前的数据对象号在AUL/MyDUL中是很容易找出来的. 到此已经说明了如何恢复Truncate表了.