总体思路: truncate后只是元数据修改了找不到数据了,但是只要还没覆盖,物理层面的数据还在,只要扫描所有的空块,找到属于这个对象的块,然后再通过 rowid 查询出数据, 将相关数据插入到新表中即可。 会丢数 [oracle@zc ~]$ sqlplus zc/zc SQL*Plus: Release 19.0.0.0.0 - Production on Thu May 8 09:40:47 2025 Version 19.3.0.0.0 Copyright (c) 1982, 2019, Oracle. All rights reserved. Last Successful login time: Mon Apr 28 2025 10:04:46 +08:00 Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.3.0.0.0 SQL> SQL> SQL> show parameter name NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ cdb_cluster_name string cell_offloadgroup_name string db_file_name_convert string db_name string tt db_unique_name string tt global_names boolean FALSE instance_name string tt lock_name_space string log_file_name_convert string pdb_file_name_convert string processor_group_name string NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ service_names string tt1 本机为 19c的none pdb模式 创建测试表 和 测试表的备份表 方便后面对照 SQL> create table tt as select * from all_objects; Table created. SQL> create table tt_bak as select * from tt ; Table created. SQL> select count(*) from tt; COUNT(*) ---------- 67917 SQL> select count(*) from tt_bak; COUNT(*) ---------- 67917 truncate 表并填充数据 SQL> truncate table tt ; Table truncated. SQL> insert into tt select * from tt_bak where owner='ZC'; 11 rows created. SQL> commit ; Commit complete. SQL> select rowid from tt; ROWID ------------------ AAAVk9AAFAAAAW9AAA AAAVk9AAFAAAAW9AAB AAAVk9AAFAAAAW9AAC AAAVk9AAFAAAAW9AAD AAAVk9AAFAAAAW9AAE AAAVk9AAFAAAAW9AAF AAAVk9AAFAAAAW9AAG AAAVk9AAFAAAAW9AAH AAAVk9AAFAAAAW9AAI AAAVk9AAFAAAAW9AAJ AAAVk9AAFAAAAW9AAK 11 rows selected. SQL> select get_rowid('AAAVk9AAFAAAAW9AAK') row_id from dual; ROW_ID -------------------------------------------------------------------------------- OBJECT# IS :88381 RELATIVE_FNO IS :5 BLOCK NUMBER IS :1469 ROW NUMBER IS :10 SQL> select get_rowid('AAAVk9AAFAAAAW9AAJ') row_id from dual; ROW_ID -------------------------------------------------------------------------------- OBJECT# IS :88381 RELATIVE_FNO IS :5 BLOCK NUMBER IS :1469 ROW NUMBER IS :9 FILE# NAME ---------- ------------------------------------------------------------ 1 +DATA/TT/DATAFILE/system.279.1192719849 3 +DATA/TT/DATAFILE/sysaux.278.1192719893 4 +DATA/TT/DATAFILE/undotbs1.277.1192719919 5 +DATA/TT/DATAFILE/user001.dbf 7 +DATA/TT/DATAFILE/users.276.1192719919 truncate 前 SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where object_name='TT' and owner='ZC'; OBJECT_NAME OBJECT_ID DATA_OBJECT_ID -------------------- ---------- -------------- TT 88379 88379 truncate 后 SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where object_name='TT' and owner='ZC'; OBJECT_NAME OBJECT_ID DATA_OBJECT_ID -------------------- ---------- -------------- TT 88379 88381 原始数据 sys查询 SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects AS OF TIMESTAMP TO_TIMESTAMP('2025-05-08 15:40:00', 'YYYY-MM-DD HH24:MI:SS') where object_name='TT' and owner='ZC'; TT 88379 88379 修改obj$ 的DATAOBJ 后面构造数据用 SQL> update (select * from obj$ where obj#=88379 and DATAOBJ#=88381) set DATAOBJ#=88379; 1 row updated. SQL> commit; Commit complete. 查看数据文件的最大块号: select * from dba_DATA_FILES where file_id=5; select * from dba_DATA_FILES where file_id=7; SQL> select * from dba_DATA_FILES where file_id=5; FILE_NAME -------------------------------------------------------------------------------- FILE_ID TABLESPACE_NAME BYTES BLOCKS STATUS ---------- ------------------------------ ---------- ---------- --------- RELATIVE_FNO AUT MAXBYTES MAXBLOCKS INCREMENT_BY USER_BYTES USER_BLOCKS ------------ --- ---------- ---------- ------------ ---------- ----------- ONLINE_ LOST_WR ------- ------- +DATA/TT/DATAFILE/user001.dbf 5 USERS 27262976 3328 AVAILABLE 5 YES 3.4360E+10 4194302 1 26214400 3200 ONLINE OFF SQL> select * from dba_DATA_FILES where file_id=7; FILE_NAME -------------------------------------------------------------------------------- FILE_ID TABLESPACE_NAME BYTES BLOCKS STATUS ---------- ------------------------------ ---------- ---------- --------- RELATIVE_FNO AUT MAXBYTES MAXBLOCKS INCREMENT_BY USER_BYTES USER_BLOCKS ------------ --- ---------- ---------- ------------ ---------- ----------- ONLINE_ LOST_WR ------- ------- +DATA/TT/DATAFILE/users.276.1192719919 7 USERS 32768000 4000 AVAILABLE 7 YES 3.4360E+10 4194302 160 31719424 3872 ONLINE OFF 查看bbed 配置,这里通过bbed 查询可以更好的区分需要扫描然后构造的块 [oracle@zc bbed]$ cat bbed.par password=blockedit spool=yes blocksize=8192 listfile=./filelist_with_numbers.txt mode=edit [oracle@zc bbed]$ cat filelist_with_numbers.txt [oracle@zc bbed]$ cat filelist_with_numbers.txt 5 /software/user001.dbf 7 /software/users.276.1192719919 需要将数据文件拷贝到本地 扫描这个文件,将块的对象号查询出来 seq 128 1 3328 | xargs -IQ echo p /d dba 5,Q ktbbhsid.ktbbhsg1 | bbed PARFILE=./bbed.par > /dev/null seq 128 1 4000 | xargs -IQ echo p /d dba 7,Q ktbbhsid.ktbbhsg1 | bbed PARFILE=./bbed.par > /dev/null 看看有多少块 [oracle@zc bbed]$ grep -B1 " 88379$" log.bbd | grep ktbbhsid.ktbbhsg1 |wc -l 587 将需要扫描的块保存到文件中 grep -B1 88379$ log.bbd | grep ktbbhsid.ktbbhsg1 > scan.txt 将新建的这个表的最后一部分数据块也扫描一下 grep -B1 88381$ log.bbd | grep ktbbhsid.ktbbhsg1 >> scan.txt 如果时间很久可能有部分数据都会被分配给别的表了可以考虑下面的步骤2 创建存放要扫描的块的表 以及 存放备用数据的表 create table zc.scanblock ( file_id number,block_id number ) tablespace sysaux; create table zc.bak_t tablespace sysaux as select * from zc.t where 0=1; 通过awk 修改相关文件,变成插入语句 awk '{print $5}' scan.txt | sed 's/^/insert into zc.scanblock values (/;s/$/);/' > scan1.txt [oracle@zc bbed]$ head -2 scan1.txt ; tail -2 scan1.txt insert into zc.scanblock values (5,1469); insert into zc.scanblock values (5,1470); insert into zc.scanblock values (5,1471); [oracle@zc bbed]$ @scan1.txt SQL> commit; 进行恢复的存储过程: declare v_fno number; v_s_bno number; v_e_bno number; v_rowid rowid; v_owner varchar2(100):='&&1'; v_table varchar2(100):='&&2'; v_o_owner varchar2(100):='&&3'; v_o_table varchar2(100):='&&4'; v_dataobj number; v_sql varchar2(4000); v_tablespace varchar2(100); nrows number; begin nrows:=0; select data_object_id into v_dataobj from dba_objects where owner=v_owner and object_name=v_table; -- select tablespace_name into v_tablespace from dba_tables where owner=v_owner and table_name=v_table; for i in (select file_id,block_id from zc.scanblock) loop v_fno:=i.file_id; v_s_bno:=i.block_id; v_e_bno:=i.block_id+1-1; for j in v_s_bno .. v_e_bno loop begin for x in 0 .. 200 loop v_rowid:=dbms_rowid.rowid_create(1,v_dataobj,v_fno,j,x); v_sql:='insert into '||v_o_owner||'.'||v_o_table||' select * from '||v_owner||'.'||v_table||' where rowid=:1'; execute immediate v_sql using v_rowid; if sql%rowcount = 1 then nrows:=nrows+1; end if; if (mod(nrows,10000)=0) then commit; end if; end loop; exception when others then null; end; commit; end loop; end loop; end; / alter system flush shared_pool; alter system flush buffer_cache; 上面两步一定执行 不然可能抽不出数据来 然后执行: @trun.txt ZC TT ZC BAK_T select count(*) from ZC.BAK_T; 恢复现场 update (select * from obj$ where obj#=88379 and DATAOBJ#=88379) set DATAOBJ#=88381; commit; alter system flush shared_pool; alter system flush buffer_cache; 还有一种方法: 遍历表所在tablespace的free block和该表的第一个extent,以及该表空间所有segment的最后一个extent,使用dbms_rowid.rowid_create抽取数据 declare v_fno number; v_s_bno number; v_e_bno number; v_rowid rowid; nrows number; v_owner varchar2(100):='RESCUREORA'; v_table varchar2(100):='RESCUREORA_TABLE'; v_o_owner varchar2(100):='SYS'; v_o_table varchar2(100):='RESCUREORA_TABLE'; v_dataobj number; v_sql varchar2(4000); v_tablespace varchar2(100); begin select data_object_id into v_dataobj from dba_objects where owner=v_owner and object_name=v_table; select tablespace_name into v_tablespace from dba_tables where owner=v_owner and table_name=v_table; for i in (select relative_fno,block_id,blocks from dba_extents where owner=v_owner and segment_name=v_table and extent_id=0 union all select relative_fno,block_id,blocks from dba_free_space where tablespace_name=v_tablespace union all select relative_fno,block_id,blocks from ( select relative_fno,block_id,blocks,row_number()over(partition by owner,segment_name,partition_name order by extent_id desc) rn from dba_extents where tablespace_name=v_tablespace and extent_id>0) where rn=1) loop v_fno:=i.relative_fno; v_s_bno:=i.block_id; v_e_bno:=i.block_id+i.blocks-1; for j in v_s_bno .. v_e_bno loop begin for x in 0 .. 999 loop v_rowid:=dbms_rowid.rowid_create(1,v_dataobj,v_fno,j,x); v_sql:='insert into '||v_o_owner||'.'||v_o_table||' select * from '||v_owner||'.'||v_table||' where rowid=:1'; execute immediate v_sql using v_rowid; end loop; exception when others then null; end; commit; end loop; end loop; end; / 参考文档: http://www.minniebaby.tech/2021/10/25/truncate-table%e6%81%a2%e5%a4%8d-%e8%84%9a%e6%9c%ac/ https://blog.itpub.net/267265/viewspace-3083157/
truncate 扫描数据块恢复
来源:这里教程网
时间:2026-03-03 21:57:29
作者:
编辑推荐:
下一篇:
相关推荐
-
雷神推出 MIX PRO II 迷你主机:基于 Ultra 200H,玻璃上盖 + ARGB 灯效
2 月 9 日消息,雷神 (THUNDEROBOT) 现已宣布推出基于英
-
制造商 Musnap 推出彩色墨水屏电纸书 Ocean C:支持手写笔、第三方安卓应用
2 月 10 日消息,制造商 Musnap 现已在海外推出一款 Oce
热文推荐
- 国产密码新时代!华测国密 SSL 证书解锁安全新高度
国产密码新时代!华测国密 SSL 证书解锁安全新高度
26-03-03 - 数据库管理-第325期 ADG Failover后该做啥(20250513)
- 评测揭秘!同一份外卖,拼好饭为什么更有性价比?
评测揭秘!同一份外卖,拼好饭为什么更有性价比?
26-03-03 - 京东敢烧钱做外卖,原来是因为电商赚麻了
京东敢烧钱做外卖,原来是因为电商赚麻了
26-03-03 - 深入浅出 Rust 并发:RwLock 与 Mutex 在 Tauri 项目中的实践
- 创新靶点与双机制策略崛起,AACR揭幕ADC治疗新纪元
创新靶点与双机制策略崛起,AACR揭幕ADC治疗新纪元
26-03-03 - Robotaxi新消息密集释放,量产元年来临谁在领跑?
Robotaxi新消息密集释放,量产元年来临谁在领跑?
26-03-03 - Oracle集群+ACFS竟让OGG故障率归零,太不可思议了!
Oracle集群+ACFS竟让OGG故障率归零,太不可思议了!
26-03-03 - 刘强东上街送外卖,美团王兴还睡得着吗?
刘强东上街送外卖,美团王兴还睡得着吗?
26-03-03 - 【KWDB 创作者计划】_KWDB引领数据库技术革新的璀璨之星
【KWDB 创作者计划】_KWDB引领数据库技术革新的璀璨之星
26-03-03
