Skip to content

 

REM NAME: salvage.sql
REM NOTE: This program will not handle long, long raw colums or chained rows.
SET ECHO OFF;  
SET SERVEROUTPUT ON;  
  
CREATE OR REPLACE FUNCTION dectohex(a IN NUMBER) RETURN VARCHAR2 IS  
      x VARCHAR2(8) := '';  
        y VARCHAR2(1);  
        z NUMBER;  
        w NUMBER;  
    BEGIN  
        IF a > POWER(2,32) OR a < 0  THEN  
            RAISE invalid_number;  
        END IF;  
        w := a;  
        WHILE w > 0 LOOP  
 z := w MOD 16;  
        IF z = 10 THEN y := 'A';  
        ELSIF z = 11 THEN y := 'B';  
        ELSIF z = 12 THEN y := 'C';  
        ELSIF z = 13 THEN y := 'D';  
     ELSIF z = 14 THEN y := 'E';  
        ELSIF z = 15 THEN y := 'F';  
        ELSE y := TO_CHAR(z);  
        END IF;  
            w := TRUNC(w / 16);  
            x := CONCAT(y,x);   -- build x string backwards  
        END LOOP;  
        RETURN x;  
    END;  
/  
  
  
DECLARE  
  
        table_name         VARCHAR2(30):= 'dept';  
   user_name          VARCHAR2(30):= 'scott';      
  
        file_id            NUMBER;  
        block_id           NUMBER;  
        extentid           number;  
    blocks             NUMBER;  
        file_id_hex        VARCHAR2(4);  
        block_id_hex       VARCHAR2(8);  
        row_count_hex      VARCHAR2(4);  
        row_count          NUMBER;  
        block_counter      NUMBER;  
        for_loop_counter   NUMBER;  
        corrupt_block_flag NUMBER:=0;  
        check_file NUMBER;  
        check_block        NUMBER;  
        row_id_hex         CHAR(18);  
        invalid_rowid      EXCEPTION;  
        rows_per_block     NUMBER:=100;  
  
    CURSOR c1 IS SELECT file_id, block_id, blocks,extent_id  FROM dba_extents  
       WHERE SEGMENT_NAME = UPPER(table_name) AND OWNER = UPPER(user_name)  
    ORDER BY EXTENT_ID;  
            
    CURSOR c2 IS SELECT file_id,block_id FROM corrupt_block;  
      
    PROCEDURE ins (v_rowid VARCHAR2) is  
      bad_rowid  EXCEPTION;  
      PRAGMA EXCEPTION_INIT (bad_rowid, -01410);  
      BEGIN  
   INSERT INTO salvaged_rows_table  
                SELECT *  
                FROM SCOTT.DEPT  
                WHERE rowid = v_rowid;  
  
      EXCEPTION  
         WHEN bad_rowid THEN  
         NULL;  
    END ins;  
  
BEGIN  
  OPEN c1;  
  LOOP   
    FETCH c1 INTO file_id, block_id, blocks,extentid;  
    EXIT WHEN c1%NOTFOUND;  
    dbms_output.put_line('extent: file:'||to_char(file_id)||'  
block_id:'||to_char(block_id)||' blocks:'||to_char(blocks));  
    file_id_hex := DECTOHEX(file_id);  
    block_counter:= block_id;  
    for_loop_counter:= block_id + blocks-1;  
    FOR i IN block_id..for_loop_counter LOOP  
      corrupt_block_flag:= 0;  
      OPEN c2;  
        LOOP  
          FETCH c2 INTO check_file, check_block;  
          EXIT WHEN c2%NOTFOUND;  
          IF (check_block = block_counter AND check_file = file_id) THEN  
             corrupt_block_flag :=1;  
               dbms_output.put_line('....Skipping corrupt  
block:'||to_char(block_counter)|| ' file id:'||to_char(file_id));  
          END IF;  
        END LOOP;    
      CLOSE c2;  
      block_id_hex := dectohex(block_counter);  
      IF corrupt_block_flag = 0 THEN  
        FOR row_count IN 0..rows_per_block LOOP  
            IF row_count = 0 THEN  
               row_count_hex := '0';  
            ELSE  
               row_count_hex:=dectohex(row_count);    
      END IF;  
            row_id_hex:=LPAD(block_id_hex,8,0) ||'.'||  
    LPAD(row_count_hex,4,0) ||'.'|| LPAD(file_id_hex,4,0);  
            ins(row_id_hex);  
        END LOOP;  
      END IF;  
      block_counter := block_counter+1;  
      COMMIT;  
    END LOOP;       
  END LOOP;  
CLOSE c1;  
COMMIT;  
END;  
/
SALVAGE.SQL – PL/SQL Code to Extract Data from a Corrupt Table

利用构造ROWID脚本实现无备份情况下绕过ORA-1578、8103、1410、00600 kdsgrp1 等坏块场景

利用构造rowid的方式来实现绕过坏块恢复正常的表数据库,也可以通过该脚本尝试读取坏块内部未损坏的数据,只是脚本需要修改。

 

SKIP ORA-1578 ORA-8103 ORA-1410


REM Create a new table based on the table that is producing errors with no rows:

create table <new table name>
as
select *
from   <original table name>
where  1=2;

REM Create the table to keep track of ROWIDs pointing to affected rows:

create table bad_rows (row_id rowid, oracle_error_code number);

set serveroutput on

DECLARE
TYPE RowIDTab IS TABLE OF ROWID INDEX BY BINARY_INTEGER;

CURSOR c1 IS select /*+ index_ffs(tab1 <index name>) parallel(tab1) */ rowid
from <original table name> tab1
where <indexed column> is NOT NULL
order by rowid;

r RowIDTab;
rows NATURAL := 20000;
bad_rows number := 0 ;
errors number;
error_code number;
myrowid rowid;
BEGIN
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO r LIMIT rows;
EXIT WHEN r.count=0;
BEGIN
FORALL i IN r.FIRST..r.LAST SAVE EXCEPTIONS
insert into <new table name>
select /*+ ROWID(A) */ <list of columns from table (ie col1, col2,..)>
from <original table name> A where rowid = r(i);
EXCEPTION
when OTHERS then
BEGIN
errors := SQL%BULK_EXCEPTIONS.COUNT;
FOR err1 IN 1..errors LOOP
error_code := SQL%BULK_EXCEPTIONS(err1).ERROR_CODE;
if error_code in (1410, 8103, 1578) then
myrowid := r(SQL%BULK_EXCEPTIONS(err1).ERROR_INDEX);
bad_rows := bad_rows + 1;
insert into bad_rows values(myrowid, error_code);
else
raise;
end if;
END LOOP;
END;
END;
commit;
END LOOP;
commit;
CLOSE c1;
dbms_output.put_line('Total Bad Rows: '||bad_rows);
END;
/

Notes:

  • Replace the next values in the plsql script by the values of the affected table: <index name>, <original table name>, <indexed column>, <list of columns from table (ie col1, col2,..)>
  • <index name> should be replaced by preferrable a Primary Key index.
  • If a Primary Key index is used, remove the where condition:  “where <indexed column> is NOT NULL”
  • The “order by rowid” clause is to get the rows ordered by blocks so a batch of Inserts finds the block in the buffer cache.  Otherwise, one rowid can bring one block from the table and insert just one row, the next rowid brings a different row and insert one row as opposed of finding the same previous block.
  • The idea is to get the rowid’s from an existent index, then get all the columns from the table for each rowid and insert these rows into the new table. Using the “index” hint, allows the optimizer to choose the most appropriated index to scan the table based on the indexed column.
  • Make sure that the select in the plsql is using an index. One way to verify if the index is used is to get an execution plan from sqlplus:

explain plan for
select /*+ index_ffs(tab1 <index name>) parallel(tab1) */ rowid
from <original table name> tab1
where <indexed column> is NOT NULL
order by rowid;

set lines 200
@?/rdbms/admin/utlxplp</div>


  • Note that the plsql executes an INSERT for 20000 rows and COMMIT. If it is required to change this, adjust the value of rows. e.g.:
rows NATURAL := 50000; -> to insert 50000 rows in one execution of INSERT and commit every 50000 records.
  • If 'Total Bad Rows:' displays 0 and it is known for certain that there is a block incorrect on disk that is causing the ORA-8103, , then it means that the block is empty (no rows) and there is not data loss.

SKIP ORA-600 in a Table

 

This is useful when the ORA-600 is produced by a non-existent chained row (invalid nrid) like ORA-600 [kdsgrp1] and when event 10231 does not work.


drop table bad_rows;
create table bad_rows (row_id ROWID
,oracle_error_code number);

rem Create the new empty table:

create table &&new_table
as select *
from &&affected_table
where 1=2;


set serveroutput on
declare
n number:=0;
bad_rows number := 0;
error_code number;
ora600 EXCEPTION;
PRAGMA EXCEPTION_INIT(ora600, -600);
begin
for i in (select rowid rid from  &&affected_table)  loop
begin
insert into  &&new_table
select *
from &&affected_table
where rowid=i.rid;
n:=n+1;
exception
when ora600 then
bad_rows := bad_rows + 1;
insert into bad_rows values(i.rid,600);
commit;
when others then
error_code:=SQLCODE;
bad_rows := bad_rows + 1;
insert into bad_rows values(i.rid,error_code);
commit;
end;
end loop;
dbms_output.put_line('Total Bad Rows: '||bad_rows);
dbms_output.put_line('Total Good rows: '||n);
end;
/

SKIP ORA-600 in IOT

This is useful when the ORA-600 is produced by a non-existent chained row (invalid nrid) like ORA-600 [kdsgrp1] and when event 10231 does not work for an Index Organized Table (IOT).


drop table bad_rows;
create table bad_rows (row_id UROWID
,oracle_error_code number);

rem Create the new empty table:

create table &&new_table
as select *
from &&affected_table
where 1=2;


set serveroutput on
declare
n number:=0;
bad_rows number := 0;
error_code number;
ora600 EXCEPTION;
PRAGMA EXCEPTION_INIT(ora600, -600);
begin
for i in (select /*+ INDEX_FFS(IOT_1) */ rowid rid from  &&affected_table IOT_1)  loop
begin
insert into  &&new_table
select *
from &&affected_table
where rowid=i.rid;
n:=n+1;
exception
when ora600 then
bad_rows := bad_rows + 1;
insert into bad_rows values(i.rid,600);
commit;
when others then
error_code:=SQLCODE;
bad_rows := bad_rows + 1;
insert into bad_rows values(i.rid,error_code);
commit;
end;
end loop;
dbms_output.put_line('Total Bad Rows: '||bad_rows);
dbms_output.put_line('Total Good rows: '||n);
end;
/


Oracle 恢复工具 Mdata 5.0.1 版本发布

经过2016到2018,修复了一些bug.

现在从新开放下载.企业版新增table drop后的lob恢复.

 

使用须知:

 

1.新版本下载后,建议使用jdk 1.4或者jdk 1.5运行.

2.新版不限制数据恢复量,不限制恢复行数,依然免费使用

3.新版程序绑定机器,需发邮件给我们提供运行时候的feature code,我们将根据feature code 生成注册码。

4.使用前请阅读下载文件中的使用说明书

 

联系邮箱:

 

feigigi@qq.com;

564439763@qq.com

 

下载地址:

 

Mdata person  5.0.1 Download

Oracle 12.2.0.1 and 18c 的原厂支持周期

一般我们确认oracle 数据库某个版本的支持周期都是通过mos 742060.1的文档来获悉,

MOS Note: 742060.1.: Release Schedule of Current Database Releases

但目前在该文档中目前并未提及oracle 18c的支持周期。如下图:

Clarification: Support Periods for Oracle 12.2.0.1 and 18c

 

20187月23日,Oracle在内部发布了Oracle Database 18c 18.3.0。从此日期开始,已确定上一版本的 Oracle数据库12.2.0.1支持(如下图),Oracle 12.2.0.1的修补结束日期定在2020年7月23日,据mike dietrich 描述Oracle 18c也会发生同样的事情,一旦Oracle 19c在内部可用,将确定并公布Oracle 18c的修补结束日期。从特定日期算起至少两年 – 而不是从Oracle 18c发布之日起。

 

澄清:Oracle 12.2.0.1和18c的支持期

 

在这里需要提到的是 可以通过Oracle lifetime support的手册来获取更多关于支持周期的信息

http://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf

 

关于升级至12cR2版本的Optimizer 自适应特性的设置建议

优化器自适应特性的设置是需要考虑比较慎重的一个点,oracle的产品经理 Nigel Bayliss 也公布了几篇关于此方面的设置参考,具体如下(建议看下参考文档):

情景1

从Oracle Database 11g(或更早版本)升级

将数据库升级到Oracle Database 12c第2版后,建议使用默认的自适应功能设置。为此,只需在数据库的初始化参数文件中不包含任何自适应功能参数。换句话说,不需要设置optimizer_adaptive_plansoptimizer_adaptive_statistics

把事情简单化!

 

场景2

从Oracle Database 12c第1版升级,其中应用了21171382和22652097的修补程序。

这两个补丁使Oracle Database 12c第1版数据库能够使用与Oracle Database 12c第2版相同的自适应功能设置。

可以升级带有这些补丁的Oracle Database 12c第1版数据库,而无需更改任何自适应功能设置。

或者,如果没有使用推荐的默认值预升级,并且希望在升级后使用,那么建议如下设置:

  • 从数据库初始化参数文件中删除对optimizer_adaptive_plansoptimizer_adaptive_statistics的引用。
  • 确保使用DBMS_STATS.SET_GLOBAL_PREFS将DBMS_STATS首选项
  • AUTO_STAT_EXTENSIONS设置为OFF

场景3

尚未应用从Oracle Database 12c第1版升级以及21171382和22652097的修补程序。

如果在Oracle数据库12c的第1版禁用了自适应功能(通过设置,例如,optimizer_adaptive_features到FALSE),那么而是使用Oracle数据库12c的第2版默认设置。为此,需要检查初始化参数文件,如下所示:

  • 删除对optimizer_adaptive_features参数的引用(它在Oracle Database 12c第2版中已过时)。
  • 删除用于禁用各种自适应功能的任何修复控件和隐藏参数设置。固定控制像12914055,12914055和7452863以隐藏参数等一起被典型地使用_optimizer_dsdir_usage_control_sql_plan_directive_mgmt_control
  • 无需设置optimizer_adaptive_plansoptimizer_adaptive_statistics,因为默认值是建议值。

如果在Oracle Database 12c第1版数据库中启用了自适应功能,并且想在数据库升级后以相同方式继续使用这些功能,则:

  • 从 初始化文件中删除对optimizer_adaptive_features的引用
    (在Oracle Database 12c第2版中已过时)。
  • optimizer_adaptive_statistics = TRUE添加到初始化参数文件中(并且不需要设置optimizer_adaptive_plans,因为默认值为TRUE)。
  • 使用DBMS_STATS.SET_GLOBAL_PREFS将DBMS_STATS首选项
  • AUTO_STAT_EXTENSIONS设置为ON

 

参考文档:

https://blogs.oracle.com/optimizer/optimizer-adaptive-features-and-upgrading-to-oracle-database-12c-release-2

https://blogs.oracle.com/optimizer/the-oracle-12102-october-2017-bp-and-the-adaptive-optimizer

https://mikedietrichde.com/2017/07/06/adpative-features-patches-oracle-peoplesoft/