The Join Factorization transformation was introduced in
Oracle 11g Release 2 and applies to UNION ALL queries.
In many scenarios the branches in a UNION All query share a
common processing, i.e, refer to the same tables. In the current Oracle
execution strategy, each branch of a UNION ALL query is evaluated
independently, which leads to repetitive processing, including data access and
join. The join factorization transformation offers an opportunity to share the
common computations across the UNION ALL branches
rajesh@ORA11G> create table t1 as select * from all_objects;
Table created.
rajesh@ORA11G> create table t2 as select * from t1;
Table created.
rajesh@ORA11G> create table t3 as select * from t1;
Table created.
rajesh@ORA11G> create table t4 as select * from t1;
Table created.
rajesh@ORA11G> begin
2 dbms_stats.gather_table_stats(user,'T1');
3 dbms_stats.gather_table_stats(user,'T2');
4 dbms_stats.gather_table_stats(user,'T3');
5 dbms_stats.gather_table_stats(user,'T4');
6 end;
7 /
PL/SQL procedure successfully completed.
rajesh@ORA11G>
So we have four tables loaded with some decent volume of
data.
So when a query like this goes for execution, optimizer
applies join transformation. And this is visible in explain plan
rajesh@ORA11G> set autotrace traceonly explain statistics
rajesh@ORA11G> select t1.owner, t2.object_name,
t3.object_type
2 from t1, t2, t3
3 where t1.object_id = t2.object_id
4 and t2.object_id = t3.object_id
5 and t1.object_id > 0
6 union all
7 select t1.owner, t2.object_name,
t4.object_type
8 from t1, t2, t4
9 where t1.object_id = t2.object_id
10 and t2.object_id = t4.object_id
11 and t1.object_id > 0 ;
169378 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3798759592
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------
| 0 | SELECT
STATEMENT | | 169K|
8601K| 2756 (1)|
|* 1 | HASH JOIN | | 169K|
8601K| 2756 (1)|
|* 2 | TABLE ACCESS FULL | T1 | 84689 | 909K|
338 (1)|
| 3 | VIEW | VW_JF_SET$37540893 | 169K|
6781K| 1900 (1)|
| 4 | UNION-ALL | | |
| |
|* 5 | HASH JOIN | | 84689 | 3638K|
950 (1)|
|* 6 | TABLE ACCESS FULL| T3 | 84689 | 1157K|
338 (1)|
|* 7 | TABLE ACCESS FULL| T2 | 84689 | 2481K|
338 (1)|
|* 8 | HASH JOIN | | 84689 | 3638K|
950 (1)|
|* 9 | TABLE ACCESS FULL| T4 | 84689 | 1157K|
338 (1)|
|* 10 | TABLE ACCESS
FULL| T2 | 84689 | 2481K|
338 (1)|
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 -
access("T1"."OBJECT_ID"="ITEM_1")
2 -
filter("T1"."OBJECT_ID">0)
5 -
access("T2"."OBJECT_ID"="T3"."OBJECT_ID")
6 -
filter("T3"."OBJECT_ID">0)
7 -
filter("T2"."OBJECT_ID">0)
8 -
access("T2"."OBJECT_ID"="T4"."OBJECT_ID")
9 -
filter("T4"."OBJECT_ID">0)
10 -
filter("T2"."OBJECT_ID">0)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
7178 consistent gets
2419 physical reads
0 redo size
5559340 bytes sent via SQL*Net to client
12918 bytes received via SQL*Net from client
1131 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
169378 rows processed
rajesh@ORA11G>
rajesh@ORA11G> set autotrace off
So what happened is optimizer scanned table T1 only once, not twice as
provided in union all queries. So given this query
select t1.owner, t2.object_name, t3.object_type
from t1, t2, t3
where t1.object_id = t2.object_id
and t2.object_id = t3.object_id
and t1.object_id > 0
union all
select t1.owner, t2.object_name, t4.object_type
from t1, t2, t4
where t1.object_id = t2.object_id
and t2.object_id = t4.object_id
and t1.object_id > 0 ;
Appliying join factorization, it’s transformed into this. So that table
T1 is scanned once not twice.
select t1.owner, t.object_name, t.object_type
from t1, (
select
t2.object_name, t3.object_type , t2.object_id
from
t2, t3
where
t2.object_id = t3.object_id
union
all
select
t2.object_name, t4.object_type , t2.object_id
from
t2, t4
where
t2.object_id = t4.object_id ) t
where t1.object_id = t.object_id
and t1.object_id > 0
Now comparing this explain plan with explain plan produced without join
factorization
·
Table “T1” got scanned twice.
·
Overall logical IO got increases by 16%
·
Overall query cost increased from 2756 to 3187.
rajesh@ORA11G> alter session set
"_optimizer_join_factorization"=false;
Session altered.
rajesh@ORA11G> set autotrace traceonly explain statistics
rajesh@ORA11G> select t1.owner, t2.object_name,
t3.object_type
2 from t1, t2, t3
3 where t1.object_id = t2.object_id
4 and t2.object_id = t3.object_id
5 and t1.object_id > 0
6 union all
7 select t1.owner, t2.object_name,
t4.object_type
8 from t1, t2, t4
9 where t1.object_id = t2.object_id
10 and t2.object_id = t4.object_id
11 and t1.object_id > 0 ;
169378 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 4252849160
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT
STATEMENT | |
169K| 9097K| |
3187 (1)| 00:00:39 |
| 1 | UNION-ALL | |
| | | | |
|* 2 | HASH JOIN |
| 84689 | 4548K| 2152K|
1594 (1)| 00:00:20 |
|* 3 | TABLE ACCESS FULL | T3 | 84689 |
1157K| | 338
(1)| 00:00:05 |
|* 4 | HASH JOIN |
| 84689 | 3390K| 1904K|
938 (1)| 00:00:12 |
|* 5 | TABLE ACCESS FULL| T1 | 84689 |
909K| | 338
(1)| 00:00:05 |
|* 6 | TABLE ACCESS FULL| T2 | 84689 |
2481K| | 338
(1)| 00:00:05 |
|* 7 | HASH JOIN |
| 84689 | 4548K| 2152K|
1594 (1)| 00:00:20 |
|* 8 | TABLE ACCESS FULL | T4 | 84689 |
1157K| | 338
(1)| 00:00:05 |
|* 9 | HASH JOIN |
| 84689 | 3390K| 1904K|
938 (1)| 00:00:12 |
|* 10 |
TABLE ACCESS FULL| T1 | 84689
| 909K| |
338 (1)| 00:00:05 |
|* 11 | TABLE ACCESS
FULL| T2 | 84689 | 2481K|
| 338 (1)| 00:00:05 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 -
access("T2"."OBJECT_ID"="T3"."OBJECT_ID")
3 -
filter("T3"."OBJECT_ID">0)
4 -
access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
5 -
filter("T1"."OBJECT_ID">0)
6 -
filter("T2"."OBJECT_ID">0)
7 -
access("T2"."OBJECT_ID"="T4"."OBJECT_ID")
8 -
filter("T4"."OBJECT_ID">0)
9 -
access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
10 -
filter("T1"."OBJECT_ID">0)
11 -
filter("T2"."OBJECT_ID">0)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
8391 consistent gets
2418 physical reads
0 redo size
5559340 bytes sent via SQL*Net to client
12918 bytes received via SQL*Net from client
1131 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
169378 rows processed
rajesh@ORA11G>
rajesh@ORA11G> set autotrace off
Join factorization is performed under cost-based transformation
framework; this means that we cost the plans with and without join
factorization and choose the cheapest plan.
In this case the cost of Join factorization query is 2756, but without
join factorization cost is 3187, where join factorization seems to be the
cheapest so the optimizer adopts to join factorization transformation