In a recent customer project, I was suffering to delete millions of rows from a LOG table.
Removing 3 months of data took more than 30 minutes with the best optimization possible.
After a while, I remembered that a very nice oracle feature existing since Oracle 12cR1 enables us to archive rows automatically.
Let’s me explain you how it works:
I have a big table with 10 millions of rows:
SQL> create table bigtable
segment creation immediate
nologging
as
with generator as (
select
rownum id
from dual
connect by
level <= 10000000
)
select
rownum id,
mod(rownum-1,3) val1,
mod(rownum-1,10) val2,
lpad('x',100,'x') padding
from
generator v1
order by
dbms_random.value
21 ;
Table created.
SQL> exec dbms_stats.gather_table_stats('SYS','BIGTABLE');
PL/SQL procedure successfully completed.
SQL> desc bigtable
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
VAL1 NUMBER
VAL2 NUMBER
PADDING VARCHAR2(100)
SQL> select count(*) from bigtable;
COUNT(*)
----------
10000000
SQL>
Let’s see the data dispersion related to VAL2 column:
SQL> select count(*),val2 from bigtable group by val2;
COUNT(*) VAL2
---------- ----------
1000000 6
1000000 1
1000000 7
1000000 8
1000000 2
1000000 4
1000000 5
1000000 9
1000000 3
1000000 0
10 rows selected.
SQL>
Let’s deleting rows related to VAL2 value = 8 :
SQL> set timing on
SQL> delete from bigtable where val2=8;
SQL > commit;
1000000 rows deleted.
Elapsed: 00:00:02.97
SQL> explain plan for delete from bigtable where val2=8;
Explained.
Elapsed: 00:00:00.00
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1385633249
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1000K| 2929K| 44506 (1)| 00:00:02 |
| 1 | DELETE | BIGTABLE | | | | |
|* 2 | TABLE ACCESS FULL| BIGTABLE | 1000K| 2929K| 44506 (1)| 00:00:02 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
---------------------------------------------------
2 - filter("VAL2"=8)
14 rows selected.
Elapsed: 00:00:00.02
SQL>
The rows have been removed and the oracle optimizer does a Full Scan to access the data because there is no Index on the VAL2 column. The DELETE sql statement took about 3 sec to execute.
For any reason If you want to restore this “deleting” rows, only “flashback” or “restore” from a backup will help you.
Now let’s enabling ROW ARCHIVAL on the table BIGTABLE:
SQL> alter table bigtable row archival;
Table altered.
Elapsed: 00:00:00.05
SQL>
A new hidden column called ora_archive_state is created indicating whether a row is archived or not. The column is displayed only via the SELECT command, not in the DESC command.
SQL> desc bigtable
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
VAL1 NUMBER
VAL2 NUMBER
PADDING VARCHAR2(100)
SQL> select ID,VAL1,VAL2,PADDING,ora_archive_state from bigtable where rownum = 1;
ID VAL1 VAL2 PADDING ORA_ARCHIVE_STATE
--------------------------------------------------------------------------------
4091832 2 1 xxxxxxxxxxx 0
Elapsed: 00:00:00.00
SQL>
The value 0 in the ora_archive_state means the row is not archived, thus visible via the SELECT.
Let’s confirm all rows are active (not archived):
SQL> select count(*) from bigtable;
COUNT(*)
----------
10000000
Now let’s suppose we want to archive all rows having value VAL2=6.
Lets’s modify the value of ora_archive_state:
SQL> update bigtable set ora_archive_state=dbms_ilm.archivestatename(1) where val2=6;
1000000 rows updated.
Elapsed: 00:00:37.94
SQL> commit;
Commit complete.
Elapsed: 00:00:01.28
SQL>
Let’s count the number of rows active now:
SQL> select count(*) from bigtable;
COUNT(*)
----------
8000000
Elapsed: 00:00:02.17
SQL>
The rows archived are no more visible, they have been deleted “logically” (or archived):
If we want to view all rows archived or not, set the archival visibility to ALL for the session:
SQL> alter session set row archival visibility = all;
Session altered.
Elapsed: 00:00:00.00
SQL> select count(*) from bigtable;
COUNT(*)
----------
90000000
SQL> select count(*),val2,ora_archive_state from bigtable group by val2, ora_archive_state;
COUNT(*) VAL2 ORA_ARCHIVE_STATE
--------------------------------------------------------------------------------
1000000 1 0
1000000 4 0
1000000 6 1
1000000 3 0
1000000 5 0
1000000 9 0
1000000 7 0
1000000 2 0
1000000 0 0
9 rows selected.
To see only rows unarchived, set the archival visibility to ACTIVE:
SQL> alter session set row archival visibility = active;
Session altered.
Elapsed: 00:00:00.00
SQL> select count(*),val2,ora_archive_state from bigtable group by val2, ora_archive_state;
COUNT(*) VAL2 O
---------- ---------- -
1000000 1 0
1000000 7 0
1000000 2 0
1000000 4 0
1000000 5 0
1000000 9 0
1000000 3 0
1000000 0 0
8 rows selected.
CONCLUSION
Oracle Database Row Archiving enables you to archive and delete “logically” the rows.
Generally, it can be used to archive rows in big table.
The implementation of this feature is very easy and can help you when you have to deal archiving/deleting rows in big table.