Home » Recover a Table from Drop/Truncate/Delete done on Primary using Flashback on a Standby

Recover a Table from Drop/Truncate/Delete done on Primary using Flashback on a Standby

by tuanlp

 In this article, we will learn steps needed to perform recovery of User wrong operation like delete/drop/truncate action on the production table without using Restore and Recover command on PRIMARY DATABASE.

Recovering the table from the standby database will also eliminate any required downtime on the primary database.
For this, you should have an up and running Standby database with Flashback On feature.
Please note the 
time when Drop/Truncate/Delete operation happened should be within the db_flashback_retention_target and all the flashback and archive logs should be available. Below is step by step recover a table.

First, perform the below commands on PRIMARY DATABASE.

SQL> set lines 300
SQL> col name for a10
SQL> select NAME,DATABASE_ROLE,OPEN_MODE,LOG_MODE,FLASHBACK_ON,FORCE_LOGGING,PROTECTION_MODE,PROTECTION_LEVEL,SWITCHOVER_STATUS from v$database;
 
NAME       DATABASE_ROLE    OPEN_MODE            LOG_MODE     FLASHBACK_ON       FOR PROTECTION_MODE      PROTECTION_LEVEL     SWITCHOVER_STATUS
---------- ---------------- -------------------- ------------ ------------------ --- -------------------- -------------------- --------------------
PROD01       PRIMARY          READ WRITE           ARCHIVELOG   NO                 YES MAXIMUM PERFORMANCE  MAXIMUM PERFORMANCE  SESSIONS ACTIVE


Execute the below commands on STANDBY DATABASE.

SQL> show parameter db_recovery
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_recovery_file_dest                string      M:\FLASH_RECOVERY_AREA
db_recovery_file_dest_size           big integer 80G
 
SQL> show parameter retention
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_flashback_retention_target        integer     7200
undo_retention                       integer     86400
 
SQL> set lines 300
SQL> col name for a10
SQL> select NAME,DATABASE_ROLE,OPEN_MODE,LOG_MODE,FLASHBACK_ON,FORCE_LOGGING,PROTECTION_MODE,PROTECTION_LEVEL,SWITCHOVER_STATUS from v$database;
 
NAME       DATABASE_ROLE    OPEN_MODE            LOG_MODE     FLASHBACK_ON       FOR PROTECTION_MODE      PROTECTION_LEVEL     SWITCHOVER_STATUS
---------- ---------------- -------------------- ------------ ------------------ --- -------------------- -------------------- --------------------
PROD01       PHYSICAL STANDBY READ ONLY WITH APPLY ARCHIVELOG   YES                YES MAXIMUM PERFORMANCE  MAXIMUM PERFORMANCE  NOT ALLOWED
 
 
SQL> set lines 200
SQL> set pages 200
SQL> select inst_id,process,status,client_process,THREAD#,sequence#,block#,active_agents,known_agents from gv$managed_standby where PROCESS like 'MRP%' or client_process like 'LGWR';
 
   INST_ID PROCESS   STATUS       CLIENT_P    THREAD#  SEQUENCE#     BLOCK# ACTIVE_AGENTS KNOWN_AGENTS
---------- --------- ------------ -------- ---------- ---------- ---------- ------------- ------------
         1 RFS       IDLE         LGWR              1        806     343899             0            0
         1 MRP0      APPLYING_LOG N/A               1        806     343898            33           33


Suppose in PRIMARY DATABASE developer by mistake truncate table TEST_RECOVERY on “21-dec-20 10:50:08” and approach DBA to recover the table, So you can follow the below steps on the STANDBY DATABASE to recover the truncated table. Please note while doing flashback on STANDBY DATABASE, we went back in time by 1 min ‘21-dec-20 10:49:00‘.

Execute the below commands on PRIMARY DATABASE where we truncate table TEST_RECOVERY.

SQL> select count(*) from test_recovery;
 
  COUNT(*)
----------
       100
        
SQL> truncate table test_recovery;
 
Table truncated.
 
SQL> select to_char(sysdate,'dd-mon-yy hh24:mi:ss') from dual;
 
TO_CHAR(SYSDATE,'DD-MON-YYH
---------------------------
21-dec-20 10:50:08


Execute all commands in STANDBY DATABASE.

SQL> alter database recover managed standby database cancel;
 
Database altered.
 
 
SQL> create restore point flashback_point guarantee flashback database;
 
Restore point created.
 
 
SQL> col name for a50
SQL> set lines 300
SQL> select name, to_char(scn), time from v$restore_point;
 
NAME                                               TO_CHAR(SCN)                             TIME
-------------------------------------------------- ---------------------------------------- ---------------------------------------------------------------------------
FLASHBACK_POINT                                    13610647843                              21-DEC-20 10.53.02.000000000 AM
 
 
SQL> shut immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
 
 
SQL>  startup mount
ORACLE instance started.
 
Total System Global Area 4275781632 bytes
Fixed Size                  2288096 bytes
Variable Size            1509951008 bytes
Database Buffers         2751463424 bytes
Redo Buffers               12079104 bytes
Database mounted.
 
 
SQL>  flashback database to timestamp TO_TIMESTAMP( '2020-12-21 10:49:00','YYYY-MM-DD HH24:MI:SS');
 
Flashback complete.
 
SQL>  alter database activate standby database;
 
Database altered.
 
 
SQL>  select controlfile_type from v$database;
 
CONTROL
-------
CURRENT
 
 
SQL> select status from v$instance;
 
STATUS
------------
MOUNTED
 
 
SQL> alter database open;
 
Database altered.
 
 
SQL> select count(*) from test_recovery;
 
  COUNT(*)
----------
       100


Please use EXPDP utility to take a backup of table TEST_RECOVERY from STANDBY DATABASE and import in PRIMARY DATABASE, Once export-import is done follow the below steps to convert DB to Standby, all commands in STANDBY DATABASE.

SQL>  startup mount force;
ORACLE instance started.
 
Total System Global Area 4275781632 bytes
Fixed Size                  2288096 bytes
Variable Size            1509951008 bytes
Database Buffers         2751463424 bytes
Redo Buffers               12079104 bytes
Database mounted.
 
 
SQL>  flashback database to restore point flashback_point;
 
Flashback complete.  
SQL>  alter database convert to physical standby;
 
Database altered.
 
 
SQL>  startup mount force;
ORACLE instance started.
 
Total System Global Area 4275781632 bytes
Fixed Size                  2288096 bytes
Variable Size            1509951008 bytes
Database Buffers         2751463424 bytes
Redo Buffers               12079104 bytes
Database mounted.
 
 
SQL> select controlfile_type from v$database;
 
CONTROL
-------
STANDBY
 
 
SQL>  drop restore point flashback_point;
 
Restore point dropped.
 
 
SQL> alter database open;
 
Database altered.
 
 
SQL> alter database recover managed standby database using current logfile disconnect from session;
 
Database altered.
 
 
SQL> set lines 200
SQL> set pages 200
SQL> select inst_id,process,status,client_process,THREAD#,sequence#,block#,active_agents,known_agents from gv$managed_standby where PROCESS like 'MRP%' or client_process like 'LGWR';
 
   INST_ID PROCESS   STATUS       CLIENT_P    THREAD#  SEQUENCE#     BLOCK# ACTIVE_AGENTS KNOWN_AGENTS
---------- --------- ------------ -------- ---------- ---------- ---------- ------------- ------------
         1 RFS       IDLE         LGWR              1        809        530             0            0
         1 MRP0      APPLYING_LOG N/A               1        809        529            33           33
 
 
SQL> /
 
   INST_ID PROCESS   STATUS       CLIENT_P    THREAD#  SEQUENCE#     BLOCK# ACTIVE_AGENTS KNOWN_AGENTS
---------- --------- ------------ -------- ---------- ---------- ---------- ------------- ------------
         1 RFS       IDLE         LGWR              1        809        534             0            0
         1 MRP0      APPLYING_LOG N/A               1        809        533            33           33

In this final step, we just validate the row count of a table in PRIMARY DATABASE.

SQL> select count(*) from test_recovery;
 
  COUNT(*)
----------
       100
        
 
SQL> select to_char(sysdate,'dd-mon-yy hh24:mi:ss') from dual;
 
TO_CHAR(SYSDATE,'DD-MON-YYH
---------------------------
21-dec-20 11:38:17

 

This document is only for learning purpose and always validate in the LAB environment first before applying in the LIVE environment.


Hope so you like this article!

You may also like