This article will be discussing the Flashback Query feature of Oracle database. As the name suggests, this feature is about querying database at a previous point-in-time. We can provide a previous time while executing a query, and the database will show results that would have shown at that time.
Let’s say HR updates salaries of all employees at 10:00 am. Later on, it is discovered that there was a problem with the increment formula, and the salaries were not updated as expected. Now HR wants to restore just the salary column without disturbing other data in table.
This can be done using Oracle Flashback Query. We will query salary information at 09:59 am, create a temporary table out of it, and then update main table with this salary information.
First we need to enable Flashback feature. We need to take care of following prerequisites to use this feature. This article will use ORCL as CDB and PDBORCL as PDB, but following code will work perfectly on non-container database.
Prerequisites
- Database should be in archivelog mode
- Database should have flashback on
SQL> CONN / AS SYSDBA
Connected.
SQL> SHUTDOWN IMMEDIATE;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> STARTUP MOUNT;
ORACLE instance started.
Total System Global Area 947847168 bytes
Fixed Size 2295176 bytes
Variable Size 603982456 bytes
Database Buffers 335544320 bytes
Redo Buffers 6025216 bytes
Database mounted.
SQL> ALTER DATABASE ARCHIVELOG;
Database altered.
SQL> ALTER DATABASE FLASHBACK ON;
Database altered.
SQL> ALTER DATABASE OPEN;
Database altered.
Example
Let’s create our table and insert some data in it:
SQL> CONN SYS@PDBORCL AS SYSDBA
Connected.
SQL> ALTER PLUGGABLE DATABASE PDBORCL OPEN;
Pluggable database altered.
SQL> CREATE USER USER1 IDENTIFIED BY USER1 CONTAINER=CURRENT
2 DEFAULT TABLESPACE USERS QUOTA UNLIMITED ON USERS;
User created.
SQL> GRANT CONNECT, RESOURCE TO USER1;
Grant succeeded.
SQL> CONN USER1/USER1@PDBORCL
Connected.
SQL> CREATE TABLE EMP (
2 EMPNO NUMBER,
3 ENAME VARCHAR2(10),
4 SALDATE DATE,
5 SAL NUMBER
6 );
Table created.
SQL>
SQL> INSERT INTO EMP (EMPNO, ENAME, SALDATE, SAL)
2 VALUES (10, 'JOHN', TO_DATE('01-01-2012', 'DD-MM-YYYY'), 5000);
1 row created.
SQL>
SQL> INSERT INTO EMP (EMPNO, ENAME, SALDATE, SAL)
2 VALUES (11, 'MARK', TO_DATE('17-03-2012', 'DD-MM-YYYY'), 4500);
1 row created.
SQL>
SQL> INSERT INTO EMP (EMPNO, ENAME, SALDATE, SAL)
2 VALUES (12, 'KING', TO_DATE('11-02-2012', 'DD-MM-YYYY'), 7000);
1 row created.
SQL>
SQL> COMMIT;
Commit complete.
SQL> SELECT EMPNO, ENAME, SAL
2 FROM EMP;
EMPNO ENAME SAL
----- ----- ----------
10 JOHN 5000
11 MARK 4500
12 KING 7000
SQL> SELECT TO_CHAR(SYSDATE, 'HH24:MI:SS') CURR_TIME
2 FROM DUAL;
CURR_TIM
--------
09:59:00
Now let’s give all employee a raise with an erroneous formula:
SQL> UPDATE EMP
2 SET SAL=SAL + (SAL/100*EMPNO);
3 rows updated.
SQL> COMMIT;
Commit complete.
SQL> SELECT TO_CHAR(SYSDATE, 'HH24:MI:SS') CURR_TIME
2 FROM DUAL;
CURR_TIM
--------
10:00:03
SQL> SELECT EMPNO, ENAME, SAL
2 FROM EMP;
EMPNO ENAME SAL
----- ----- ----------
10 JOHN 5500
11 MARK 4495
12 KING 7840
Now we want to recover salaries that were at 09:59:00. Let’s use Flashback query to retrieve it. Flashback queries use the “AS OF TIMESTAMP” clause to specify time and query data that would have show at that time.
SQL> CREATE TABLE TEMP_EMP AS
2 SELECT EMPNO, SAL
3 FROM EMP AS OF TIMESTAMP
4 TO_TIMESTAMP('02-01-2014 09:59:00', 'DD-MM-YYYY HH24:MI:SS');
Table created.
SQL> SELECT * FROM TEMP_EMP;
EMPNO SAL
----- ----------
10 5000
11 4500
12 7000
So we have our salaries back, and the only thing left is to update them in our main EMP table. Let’s update EMP table
SQL> UPDATE EMP E
2 SET E.SAL = (SELECT SAL FROM TEMP_EMP WHERE EMPNO=E.EMPNO);
3 rows updated.
SQL> COMMIT;
Commit complete.
SQL> SELECT EMPNO, ENAME, SAL
2 FROM EMP;
EMPNO ENAME SAL
----- ----- ----------
10 JOHN 5000
11 MARK 4500
12 KING 7000
Now we have our salaries back as they were at 09:59:00 by using the Oracle Flashback query feature. But we have limit on going back in time. The database will only retain flashback logs for minutes specified in db_flashback_retention_target initialization parameter, and it can only go as back as it has flashback logs. By default it is 1440 minutes, which is 1 day. So you need to update this parameter if you want to go further back in time.
Link to script that contains the examples in this post.