You are missing the feature of partition pruning that is crutial for large partitioned tables. Check the concepts in the documentation first.
To demonstrate the partition pruning the following setup will be used
create table TABLE1
PARTITION BY RANGE (EFF_DATE)
INTERVAL(NUMTODSINTERVAL(1, 'DAY'))
(
PARTITION p_init VALUES LESS THAN (TO_DATE('01-01-2019', 'DD-MM-YYYY'))
)
as
select rownum tb_id,
date'2019-01-01' + (rownum -1) as EFF_DATE
from dual connect by level <= 365;
The table is interval partitioned with one day partitions on the column EFF_DATE
. The TABLE2
is created identically.
Now let's look on the execution plan of your query, referencing the tables and not the partitions:
SELECT
TB1.TB_ID,
TB1.EFF_DATE
FROM TABLE1 TB1, TABLE2 TB2
WHERE
TB1.EFF_DATE >= TO_DATE(:p_req_date, 'yyyymmdd')
AND
TB1.EFF_DATE < TO_DATE(:p_req_date, 'yyyymmdd') + 1
AND
TB1.TB_ID = TB2.TB_ID;
See here how to produce the following execution plan.
Plan hash value: 3726328978
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 32 | 3724 (1)| 00:00:01 | | |
|* 1 | FILTER | | | | | | | |
|* 2 | HASH JOIN | | 2 | 32 | 3724 (1)| 00:00:01 | | |
| 3 | PARTITION RANGE ITERATOR| | 2 | 24 | 1849 (1)| 00:00:01 | KEY | KEY |
|* 4 | TABLE ACCESS FULL | TABLE1 | 2 | 24 | 1849 (1)| 00:00:01 | KEY | KEY |
| 5 | PARTITION RANGE ALL | | 365 | 1460 | 1874 (1)| 00:00:01 | 1 |1048575|
| 6 | TABLE ACCESS FULL | TABLE2 | 365 | 1460 | 1874 (1)| 00:00:01 | 1 |1048575|
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TO_DATE(:P_REQ_DATE,'yyyymmdd')+1>TO_DATE(:P_REQ_DATE,'yyyymmdd'))
2 - access("TB1"."TB_ID"="TB2"."TB_ID")
4 - filter("TB1"."EFF_DATE"<TO_DATE(:P_REQ_DATE,'yyyymmdd')+1 AND
"TB1"."EFF_DATE">=TO_DATE(:P_REQ_DATE,'yyyymmdd'))
The relevant part is in the columns Pstart
and Pstop
. You see that in the TABLE1
only one partition is accessed. KEY - KEY
means that the partition is not fixed (you used bind variables), but only data from one partition will be scanned.
The problem is on table TABLE2
which is accessed fully (all partitions).
Why?
Oracle does not know that the rows with the same ID
in both tables have the same effective date.
This is exect what you want to teach Oracle by passing the identical partition names in the join.
Much better way is to do in the WHERE
clause by adding the predicate on EFF_DATE
for the TABLE2
SELECT
TB1.TB_ID,
TB1.EFF_DATE
FROM TABLE1 TB1, TABLE2 TB2
WHERE
TB1.EFF_DATE >= TO_DATE(:p_req_date, 'yyyymmdd')
AND
TB1.EFF_DATE < TO_DATE(:p_req_date, 'yyyymmdd') + 1
AND
TB1.TB_ID = TB2.TB_ID
AND
TB2.EFF_DATE >= TO_DATE(:p_req_date, 'yyyymmdd') /* filter for EFF_DATE in TABLE2 added */
AND
TB2.EFF_DATE < TO_DATE(:p_req_date, 'yyyymmdd') + 1;
Now Oracle knows that in both tables only one partition should be accessed.
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 48 | 3724 (1)| 00:00:01 | | |
|* 1 | FILTER | | | | | | | |
|* 2 | HASH JOIN | | 2 | 48 | 3724 (1)| 00:00:01 | | |
| 3 | PARTITION RANGE ITERATOR| | 2 | 24 | 1849 (1)| 00:00:01 | KEY | KEY |
|* 4 | TABLE ACCESS FULL | TABLE1 | 2 | 24 | 1849 (1)| 00:00:01 | KEY | KEY |
| 5 | PARTITION RANGE ITERATOR| | 2 | 24 | 1874 (1)| 00:00:01 | KEY | KEY |
|* 6 | TABLE ACCESS FULL | TABLE2 | 2 | 24 | 1874 (1)| 00:00:01 | KEY | KEY |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TO_DATE(:P_REQ_DATE,'yyyymmdd')+1>TO_DATE(:P_REQ_DATE,'yyyymmdd'))
2 - access("TB1"."TB_ID"="TB2"."TB_ID")
4 - filter("TB1"."EFF_DATE"<TO_DATE(:P_REQ_DATE,'yyyymmdd')+1 AND
"TB1"."EFF_DATE">=TO_DATE(:P_REQ_DATE,'yyyymmdd'))
6 - filter("TB2"."EFF_DATE"<TO_DATE(:P_REQ_DATE,'yyyymmdd')+1 AND
"TB2"."EFF_DATE">=TO_DATE(:P_REQ_DATE,'yyyymmdd'))
So you solved your task without a need of dynamic SQL, which is always the prefered way to go.