1. Subquery Factoring
Oracle introduce WITH clause in the query starting from Oracle 9i. In few articles that I found while googling,
we can use this WITH clause to write subquery factoring or recursive query.
In this chance, I would like to show you the benefit of “Subquery Factoring” against Full Table Scan and also we
can see the same method against Unique Index Scan.
For the first case, I would like to show the comparison between the traditional and subquery factoring methods
for Full Table Scan operation and for the second case; for Unique Index Scan. The objective is to see the
reduction in LIO and PIO for MINUS operation which use FTS.
4. 799460311
30228789
2748029
28
8
41220406
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed
Please see the highlighted parts, the PIO and LIO reduced by 100% (half) when we use subquery factoring. It
can be happened because Oracle scans SUBSCRIBER only once and keeps the result in the memory and it will
be reused for second, third operation and so on. If we see more in the execution plan, there is increment in the
TEMP space usage, because when needed, Oracle will spill the information/data from the buffer cache into the
Temporary Tablespace. This is acceptable if there is enough space on the Temporary Tablespace and also fast
enough disk. Due to this “temporary” operation, Oracle produce more redo information (approx.. 300kB in this
test case)
5. Subquery Factoring for Index Unique Scan
Original Query
SQL>
2
3
4
5
select subscriber_no from tksappo.subscriber
where subscriber_no = 7266421
minus
select subscriber_no from tksappo.subscriber
where subscriber_no = 6566380;
Elapsed: 00:00:00.06
Execution Plan
----------------------------------------------------------------------------------------------------------------------------| Id | Operation
| Name
| Rows | Bytes | Cost |
-------------------------------------------------------------------|
0 | SELECT STATEMENT
|
|
1 |
14 |
78 |
|
1 | MINUS
|
|
|
|
|
|* 2 |
INDEX UNIQUE SCAN| SUBSCRIBER_PK |
1 |
7 |
1 |
|* 3 |
INDEX UNIQUE SCAN| SUBSCRIBER_PK |
1 |
7 |
1 |
-------------------------------------------------------------------Predicate Information (identified by operation id):
--------------------------------------------------2 - access("SUBSCRIBER_NO"=7266421)
3 - access("SUBSCRIBER_NO"=6566380)
Note
----- cpu costing is off (consider enabling it)
Statistics
---------------------------------------------------------565 recursive calls
0 db block gets
138 consistent gets
26 physical reads
0 redo size
523 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
6. SubQuery Factoring
SQL>
2
3
4
5
6
7
8
9
10
11
12
13
14
with qry as
(
select subscriber_no,
case
when subscriber_no = 7266421 then 1
when subscriber_no = 6566380 then 2
else 0
end as ss
from tksappo.subscriber
where subscriber_no in (7266421, 6566380)
)
select subscriber_no from qry where ss = 1
minus
select subscriber_no from qry where ss = 2;
Elapsed: 00:00:00.03
Execution Plan
--------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation
| Name
| Rows | Bytes | Cost |
-----------------------------------------------------------------------------------------|
0 | SELECT STATEMENT
|
|
2 |
64 |
80 |
|
1 | TEMP TABLE TRANSFORMATION |
|
|
|
|
|
2 |
LOAD AS SELECT
|
|
|
|
|
|
3 |
INLIST ITERATOR
|
|
|
|
|
|* 4 |
INDEX UNIQUE SCAN
| SUBSCRIBER_PK
|
2 |
14 |
1 |
|
5 |
MINUS
|
|
|
|
|
|
6 |
SORT UNIQUE
|
|
2 |
32 |
40 |
|* 7 |
VIEW
|
|
2 |
32 |
2 |
|
8 |
TABLE ACCESS FULL
| SYS_TEMP_0FD9D6613_C3DD254C |
2 |
14 |
2 |
|
9 |
SORT UNIQUE
|
|
2 |
32 |
40 |
|* 10 |
VIEW
|
|
2 |
32 |
2 |
| 11 |
TABLE ACCESS FULL
| SYS_TEMP_0FD9D6613_C3DD254C |
2 |
14 |
2 |
-----------------------------------------------------------------------------------------Predicate Information (identified by operation id):
--------------------------------------------------4 - access("SUBSCRIBER_NO"=6566380 OR "SUBSCRIBER_NO"=7266421)
7 - filter("SS"=1)
10 - filter("SS"=0)
Note
----- cpu costing is off (consider enabling it)
Statistics
---------------------------------------------------------1151 recursive calls
12 db block gets
342 consistent gets
68 physical reads
1568 redo size
523 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
19 sorts (memory)
0 sorts (disk)
1 rows processed
7. We have different result when we use Unique Index Scan. There is an increment in LIO and PIO. So far what I
can see in this increment is due to:
- Temporary table creation (we don’t have it in the original method)
8. Conclusion
1. Subquery factoring is good to simplify query (more readable) and it can give significant improvement
when we are working with FTS and scan the same object over and over again.
2. There is no silver bullet in SQL tuning until unless we did some test to confirm it. In this example, FTS
and Index Scan gives different result when we rewrite the SQL using WITH clause.
3. Different version of Oracle will produce different result, all above scenarios were tested on Oracle
10.2.0.5 (PET CM database)