OK...here's a mechanism of letting you flip without interruption to existing queries.
Let's assume table T1 is our current large table
SQL> create table t1
2 as select d.* from dba_objects d,
3 ( select 1 from dual connect by level <= 200 );
Table created.
SQL>
SQL> create index t1_ix1 on t1 ( object_id ) ;
Index created.
SQL> create index t1_ix2 on t1 ( object_name ) ;
Index created.
This is "version 1" of our data. When we want to evolve to version 2, we clone the data and make our changes. For this demo, our "change" is to lowercase some of the data
SQL> create table t2
2 as select * from t1;
Table created.
SQL>
SQL> create index t2_ix1 on t2 ( object_id ) ;
Index created.
SQL> create index t2_ix2 on t2 ( object_name ) ;
Index created.
SQL> update t2 set owner = lower(owner)
2 where object_id < 100;
19600 rows updated.
SQL> commit;
Commit complete.
Now we need a mechanism to control whether users see T1 or T2. I will use a context that is available cross-session for that
SQL> create context CTX using pkg accessed globally;
Context created.
SQL> create or replace
2 package pkg is
3 procedure setver(p_ver varchar2);
4 end;
5 /
Package created.
SQL>
SQL> create or replace
2 package body pkg is
3 procedure setver(p_ver varchar2) is
4 begin
5 dbms_session.set_context('CTX','VER',p_ver);
6 end;
7 end;
8 /
Package body created.
SQL>
We now have a view that consolidates BOTH sets of data, with a predicate to limit access based on our context
SQL> create or replace view USER_DATA as
2 select * from t1
3 where sys_context('CTX','VER') = '1'
4 union all
5 select * from t2
6 where sys_context('CTX','VER') = '2';
View created.
and we are now good to go... Here's a sample query
SQL> set autotrace traceonly explain
SQL> select *
2 from USER_DATA
3 where object_id = 23;
Execution Plan
----------------------------------------------------------
Plan hash value: 194448029
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 394 | 98K| 173K (1)| 00:00:07 |
| 1 | VIEW | USER_DATA | 394 | 52008 | 402 (0)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
|* 3 | FILTER | | | | | |
| 4 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 197 | 26004 | 201 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | T1_IX1 | 197 | | 3 (0)| 00:00:01 |
|* 6 | FILTER | | | | | |
| 7 | TABLE ACCESS BY INDEX ROWID BATCHED| T2 | 197 | 26004 | 201 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | T2_IX1 | 197 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(SYS_CONTEXT('CTX','VER')='1')
5 - access("OBJECT_ID"=23)
6 - filter(SYS_CONTEXT('CTX','VER')='2')
8 - access("OBJECT_ID"=23)
Notice the FILTER lines. We immediately exclude from access one side of the UNION ALL based on the context value. So lets prove that with some test cases.
A query to T1 directly gives the following cost:
SQL> set autotrace traceonly stat
SQL> select *
2 from T1
3 where object_id = 23;
200 rows selected.
Statistics
---------------------------------------------------------
1 recursive calls
0 db block gets
217 consistent gets
0 physical reads
0 redo size
29941 bytes sent via SQL*Net to client
542 bytes received via SQL*Net from client
15 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200 rows processed
SQL> set autotrace off
So a query to USER_DATA should be the same, when we using version 1 of the data
SQL> set autotrace traceonly stat
SQL> select *
2 from USER_DATA
3 where object_id = 23;
200 rows selected.
Statistics
------------------------------------------------------
0 recursive calls
0 db block gets
217 consistent gets
0 physical reads
0 redo size
6934 bytes sent via SQL*Net to client
549 bytes received via SQL*Net from client
15 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200 rows processed
SQL> set autotrace off
And now, just a flick of a switch of our context activates the new data
SQL> exec pkg.setver('1');
PL/SQL procedure successfully completed.
SQL> set autotrace off
SQL> select owner
2 from USER_DATA
3 where object_id = 23
4 and rownum <= 10;
OWNER
------------------------------
SYS
SYS
SYS
SYS
SYS
SYS
SYS
SYS
SYS
SYS
10 rows selected.
SQL>
SQL> exec pkg.setver('2');
PL/SQL procedure successfully completed.
SQL> select owner
2 from USER_DATA
3 where object_id = 23
4 and rownum <= 10;
OWNER
------------------------------
sys
sys
sys
sys
sys
sys
sys
sys
sys
sys
10 rows selected.
When you are ready for version 3 of your data, you just truncate T1, load the new data into there, and flick the version back to 1
and so forth. Running queries are unaffected because they see the value of the context when the query started. New queries pick up the context value.
Because its a global context, you change it once in your session (the one that does the data population) and everyone sees it immediately.