Как достать данные из поврежденной таблицы при ORA-01578: Oracle data block corrupted (file ..., block ...)

dbstalker, 16 июля

Как минимум есть три варианта: SKIP_CORRUPT_BLOCKS, ROWID Range Scans и PL/SQL скрипт. Сейчас понемногу о каждом.

Выборка данных из поврежденной таблицы с использованием SKIP_CORRUPT_BLOCKS:

Подключаемся как sysdba. Переименовываем поврежденную таблицу, чтобы иметь возможность новую таблицу с сохраненными данными назвать правильным исходным именем. Выполняем:

execute dbms_repair.skip_corrupt_blocks('schema', 'table_name' );

После выполнения этой процедуры можно сделать экспорт-импорт таблицы, или создать новую таблицу путем выборки из поврежденной - CTAS (create table as select). При этом записи из битого блока выбираться не будут.

Очистка атрибутов таблицы:

execute dbms_repair.skip_corrupt_blocks('schema','table_name',
flags=dbms_repair.noskip_flag);

Использование ROWID Range Scans для выбора данных из неповрежденных блоков.

Метод описан во второй половине поста В очередной раз об ORA-00600: Internal message code, arguments: [01578] [...] [...] [] [] []. ORA-01578: Oracle data block corrupted (file ..., block ...)

Использование PL/SQL скрипта для спасения данных

Cкрипт полностью взят вот с этого поста.

  1. Создаем таблицу, где сохраним информацию о повредженных блоках: CREATE TABLE corrupt_block (file_id NUMBER, block_id NUMBER); В таблицу с помощью команды insert INTO заносим номер файла и номер блока , полученные из сообщения о ошибке ORA-01578.
  2. Создаем таблицу под названием ‘SALVAGE_ROWS_TABLE’ со storage параметрами на ваше усмотрение. Эта таблица должна иметь такую же структуру как и наша поврежденная таблица.
  3. Редактируем PL/SQL скрипт. В строчках :
    table_name VARCHAR2(30):= 'dept';
    user_name VARCHAR2(30):= 'scott';
    
    изменяем название схемы и название таблицы.
  4. Модифицируем процедуру INS, находящуюся в описательном блоке PL/SQL скрипта. В следующих строчках нужно исправить команду select * from , прописав выборку из нашей поврежденной таблицы.
    INSERT INTO salvaged_rows_table
    SELECT *
    FROM SCOTT.DEPT
    WHERE rowid = v_rowid;
    
  5. После вышеизложенных изменений можно приступить к выполнению скрипта.
    Script
    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;
    /
    

 

Новый комментарий

Я не спамер: введите суму 0+5



 

От авторов блога

О Блоге - прочитай перед началом.

Задать вопрос и получить ответ - уже решено 94 вопросов

Глоссарий - список терминов и сокращений


 
 

Бизнес форум

Последние темы:

Шины бу
26 апреля, 2 ответа
Потрібна порада
25 апреля, 2 ответа
Посоветуйте адвоката
25 апреля, 1 ответа