Optimize TO_TIMESTAMP() query within Oracle - oracle

every time I execute this query it takes like 2 minutes to execute:
select * from CPOB_Monitoring_Dashboard
where VOYAGE_STRT_DT >= TO_TIMESTAMP('2014-07-03 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF')
and VOYAGE_STRT_DT <= TO_TIMESTAMP('2018-07-03 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF')
However if I change it to use TO_DATE instead of TO_TIMESTAMP is really fast.
Linq is generating the query using TOTIMESTAMP and I've not found yet a way to change that to use TO_DATE, is there any way that I can optimize the TOTIMESTAMP query??
Here is the Execution Plan for the query using TOTIMESTAMP:
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 246273147
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 21842 | 4820K| | 1336 (1)| 00:00:17 |
| 1 | VIEW | CPOB_Monitoring_Dashboard| 21842 | 4820K| | 1336 (1)| 00:00:17 |
| 2 | HASH UNIQUE | | 21842 | 3988K| 4384K| 1336 (1)| 00:00:17 |
| 3 | NESTED LOOPS | | 21842 | 3988K| | 442 (1)| 00:00:06 |
| 4 | NESTED LOOPS | | 47 | 7661 | | 160 (1)| 00:00:02 |
|* 5 | TABLE ACCESS FULL | VOYAGE_INFO | 46 | 1012 | | 68 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID| PROCESS_CTRL | 1 | 141 | | 2 (0)| 00:00:01 |
|* 7 | INDEX RANGE SCAN | VOYAGE_ID_IDX | 1 | | | 1 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | PLY_IDX2 | 467 | 11208 | | 6 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - filter(INTERNAL_FUNCTION("CPVI"."VOYAGE_STRT_DT")>=TIMESTAMP' 2014-07-03 00:00:00.000000000' AND
INTERNAL_FUNCTION("CPVI"."VOYAGE_STRT_DT")<=TIMESTAMP' 2018-07-03 00:00:00.000000000')
7 - access("CPC"."VOYAGE_ID"="CPVI"."VOYAGE_ID")
8 - access("CPC"."BRAND_NAME"="CPOB"."BRAND_ID" AND "CPC"."SHIP_NAME"=""SHIP_NAME")
filter("CPC"."SHIP_NAME"="CPOB"."SHIP_NAME")
24 rows selected.

Related

Explanation of Oracle PARTITION BY vs GROUP BY for similar results

I have the following table (Marks):
firstname lastname Mark
------------------------------
arun prasanth 40
ann antony 45
sruthy abc 41
new abc 47
arun prasanth 45
arun prasanth 49
ann antony 49
And would like to add a column that tags if a record with specific columns occurs more than once. This is the result:
firstname lastname Mark MULTI_FLAG
----------------------------------------------
arun prasanth 40 1
ann antony 45 1
sruthy abc 41 0
new abc 47 0
arun prasanth 45 1
arun prasanth 49 1
ann antony 49 1
I can get the result with the following GROUP BY query:
SELECT M1.firstname
,M1.lastname
,M1.Mark
,M2.MULTI_COUNT
FROM Marks M1
JOIN (SELECT firstname, lastname, CASE WHEN COUNT (*) > 1 THEN 1 ELSE 0 END AS MULTI_COUNT
FROM Marks
GROUP BY firstname, lastname) M2
ON M2.firstname = M1.firstname AND M2.lastname = M1.lastname;
Or by this much prettier PARTITION BY query:
SELECT
firstname,
lastname,
CASE WHEN COUNT(*) OVER (PARTITION BY
firstname,
lastname) > 1 THEN 1 ELSE 0 END AS MULTI_FLAG
FROM
Marks
Running the GROUP BY query on a similar large table returned in:
34 m 56 s 595 ms
Running the PARTITION BY query on a similar large table returned in:
First run: 55 m 47 s 851 ms
Second run: 36 m 46 s 95 ms
I would be interested in knowing:
The best way to achieve my results
What accounts for the performance difference.
EDIT: How to read the query plan.
EDIT:
Oracle Version
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
"CORE 11.2.0.3.0 Production"
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
PARTITION BY Plan
PLAN_TABLE_OUTPUT
Plan hash value: 3822227444
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 668K| 90M| | 90429 (1)| 00:18:06 |
| 1 | WINDOW SORT | | 668K| 90M| 98M| 90429 (1)| 00:18:06 |
|* 2 | HASH JOIN RIGHT OUTER | | 668K| 90M| | 69340 (1)| 00:13:53 |
| 3 | TABLE ACCESS FULL | COUNTRY_REGION_MAPPINGS | 177 | 4779 | | 3 (0)| 00:00:01 |
| 4 | NESTED LOOPS | | | | | | |
| 5 | NESTED LOOPS | | 377K| 41M| | 69335 (1)| 00:13:53 |
| 6 | MAT_VIEW ACCESS FULL | PROJINFO_MAX_ITER_MVW | 17713 | 328K| | 782 (1)| 00:00:10 |
|* 7 | INDEX RANGE SCAN | Q_CLIN_ASSUM_BYCOUN_PK | 1 | | | 3 (0)| 00:00:01 |
| 8 | TABLE ACCESS BY INDEX ROWID| Q_CLINICAL_ASSUM_BYCOUNTRY | 21 | 2016 | | 4 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(UPPER("CRM"."COUNTRY"(+))=UPPER("QCAB"."TRIAL_COUNTRY"))
7 - access("PMIM"."OPPORTUNITYNUM"="QCAB"."OPPORTUNITYNUM" AND "PMIM"."CONTRACTNUM"="QCAB"."CONTRACTNUM"
AND "PMIM"."ITERATION"="QCAB"."ITERATION")
filter(UPPER("QCAB"."SHEET_LOC") LIKE '%COUNTRY ASSUMPTIONS%' OR UPPER("QCAB"."SHEET_LOC") LIKE
'INPUT%')
GROUP BY Plan
PLAN_TABLE_OUTPUT
Plan hash value: 648231064
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 912 | 2052K| | 226K (1)| 00:45:22 |
|* 1 | HASH JOIN | | 912 | 2052K| | 226K (1)| 00:45:22 |
| 2 | TABLE ACCESS FULL | COUNTRY_REGION_MAPPINGS | 177 | 4779 | | 3 (0)| 00:00:01 |
|* 3 | HASH JOIN | | 89667 | 194M| 45M| 226K (1)| 00:45:22 |
| 4 | NESTED LOOPS | | | | | | |
| 5 | NESTED LOOPS | | 377K| 41M| | 69335 (1)| 00:13:53 |
| 6 | MAT_VIEW ACCESS FULL | PROJINFO_MAX_ITER_MVW | 17713 | 328K| | 782 (1)| 00:00:10 |
|* 7 | INDEX RANGE SCAN | Q_CLIN_ASSUM_BYCOUN_PK | 1 | | | 3 (0)| 00:00:01 |
| 8 | TABLE ACCESS BY INDEX ROWID | Q_CLINICAL_ASSUM_BYCOUNTRY | 21 | 2016 | | 4 (0)| 00:00:01 |
| 9 | VIEW | | 668K| 1377M| | 86518 (1)| 00:17:19 |
| 10 | HASH GROUP BY | | 668K| 72M| 80M| 86518 (1)| 00:17:19 |
|* 11 | HASH JOIN RIGHT OUTER | | 668K| 72M| | 69340 (1)| 00:13:53 |
| 12 | TABLE ACCESS FULL | COUNTRY_REGION_MAPPINGS | 177 | 2478 | | 3 (0)| 00:00:01 |
| 13 | NESTED LOOPS | | | | | | |
| 14 | NESTED LOOPS | | 377K| 35M| | 69335 (1)| 00:13:53 |
| 15 | MAT_VIEW ACCESS FULL | PROJINFO_MAX_ITER_MVW | 17713 | 328K| | 782 (1)| 00:00:10 |
|* 16 | INDEX RANGE SCAN | Q_CLIN_ASSUM_BYCOUN_PK | 1 | | | 3 (0)| 00:00:01 |
| 17 | TABLE ACCESS BY INDEX ROWID| Q_CLINICAL_ASSUM_BYCOUNTRY | 21 | 1701 | | 4 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("R2"."TRIAL_COUNTRY_CD"="CRM"."COUNTRY_CD" AND
UPPER("CRM"."COUNTRY")=UPPER("QCAB"."TRIAL_COUNTRY"))
3 - access("R2"."OPPORTUNITYNUM"="QCAB"."OPPORTUNITYNUM" AND "R2"."ITERATION"="QCAB"."ITERATION" AND
"R2"."CONTRACTNUM"="QCAB"."CONTRACTNUM" AND "R2"."ASSUMPTION"="QCAB"."ASSUMPTION")
7 - access("PMIM"."OPPORTUNITYNUM"="QCAB"."OPPORTUNITYNUM" AND "PMIM"."CONTRACTNUM"="QCAB"."CONTRACTNUM" AND
"PMIM"."ITERATION"="QCAB"."ITERATION")
filter(UPPER("QCAB"."SHEET_LOC") LIKE '%COUNTRY ASSUMPTIONS%' OR UPPER("QCAB"."SHEET_LOC") LIKE 'INPUT%')
11 - access(UPPER("CRM"."COUNTRY"(+))=UPPER("QCAB"."TRIAL_COUNTRY"))
16 - access("PMIM"."OPPORTUNITYNUM"="QCAB"."OPPORTUNITYNUM" AND "PMIM"."CONTRACTNUM"="QCAB"."CONTRACTNUM" AND
"PMIM"."ITERATION"="QCAB"."ITERATION")
filter(UPPER("QCAB"."SHEET_LOC") LIKE '%COUNTRY ASSUMPTIONS%' OR UPPER("QCAB"."SHEET_LOC") LIKE 'INPUT%')
Typically you start with the analytic function count(*) which leads to a compact SQL.
The drawback of this aproach is that the data must be sorted (see WINDOW SORT operation). The GROUP BY approach avoids
the sorting as HASH GROUP BY may be used, which can lead to a better performance.
Your example is a bit more involved, as you do not use table but a view that joins three tables - this join is performed twice, for the GROUP BY and for the detail data; which
is of course not optimal.
So I'll start with the analytic function version of the query (possible with a PARALLELoption).
If you want to try the GROUP BY a lightway version is possible:
1) group only the duplicated keys
2) make OUTER JOIN to assign the MULTI_FLAG
example with execution plan below - simple test with your data
with dups as (
select firstname,lastname from tmp
group by firstname,lastname
having count(*) > 1)
select tmp.FIRSTNAME, tmp.LASTNAME, tmp.MARK,
case when dups.firstname is not NULL then 1 else 0 end as MULTI_FLAG
from tmp
left outer join dups on tmp.firstname = dups.firstname and tmp.lastname = dups.lastname;
You still need to access your view twice, but the final join will be faster (espetially if you have only small number of duplicated keys).
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 105K| 26M| | 1673 (1)| 00:00:21 |
|* 1 | HASH JOIN RIGHT OUTER| | 105K| 26M| 11M| 1673 (1)| 00:00:21 |
| 2 | VIEW | | 105K| 10M| | 128 (4)| 00:00:02 |
|* 3 | FILTER | | | | | | |
| 4 | HASH GROUP BY | | 105K| 10M| | 128 (4)| 00:00:02 |
| 5 | TABLE ACCESS FULL| TMP | 105K| 10M| | 125 (1)| 00:00:02 |
| 6 | TABLE ACCESS FULL | TMP | 105K| 15M| | 125 (1)| 00:00:02 |
--------------------------------------------------------------------------------------

how to speed up the order by query in oracle

my below pagination query runs faster (2.5 sec) without order by .
if I use order by it get slower (180 sec).
Total number of record is only 90000
select * from(
select i.*,rownum rno from (
select opp.updat,nvl(s.name,c.vemail),s.name,c.vemail
from sfa_opportunities opp,sfa_company s, customer c
where opp.companyid = c.companyid(+)
and opp.custid = c.custid(+)
and opp.companyid = s.companyid(+)
and opp.sfacompid = s.sfacompid(+)
order by 2 asc, 1 asc
)i) where rno >= 1 and rno <= 30
I have given the explain plan below for reference.
---------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 97980 | 110M| | 14137 (1)| 00:03:18 |
|* 1 | VIEW | | 97980 | 110M| | 14137 (1)| 00:03:18 |
| 2 | COUNT | | | | | | |
| 3 | VIEW | | 97980 | 109M| | 14137 (1)| 00:03:18 |
| 4 | SORT ORDER BY | | 97980 | 6602K| 15M| 14137 (1)| 00:03:18 |
| 5 | NESTED LOOPS OUTER | | 97980 | 6602K| | 13137 (1)| 00:03:04 |
|* 6 | HASH JOIN RIGHT OUTER | | 97980 | 3635K| 1136K| 614 (1)| 00:00:09 |
| 7 | TABLE ACCESS FULL | SFA_COMPANY | 34851 | 714K| | 58 (0)| 00:00:01 |
| 8 | TABLE ACCESS FULL | SFA_OPPORTUNITIES | 97980 | 1626K| | 390 (1)| 00:00:06 |
|* 9 | TABLE ACCESS BY INDEX ROWID| CUSTOMER | 1 | 31 | | 1 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | PK_CUSTOMER_CUSTID | 1 | | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RNO"<=30 AND "RNO">=1)
6 - access("OPP"."COMPANYID"="S"."COMPANYID"(+) AND "OPP"."SFACOMPID"="S"."SFACOMPID"(+))
9 - filter("OPP"."COMPANYID"="C"."COMPANYID"(+))
10 - access("OPP"."CUSTID"="C"."CUSTID"(+))
You're sorting on nvl(s.name,c.vemail), then opp.updat. My guess is the NVL may prevent a lot of optimization, because Oracle can't tell what the value of that column is going to be without looking at every row in the joined result. You could try adding indexes on those three columns or a function based index on nvl(s.name,c.vemail).

Oracle LEFT JOIN View performance

I have two tables, they aren't large tables.
I have created a View based on this tables
select
tab_a.id as id,
tab_a.name as name
from tableA as tab_a
UNION ALL
select
tab_b.id as id,
tab_b.name as name
from tableB as tab_b
After all, I have a third table, lets call it tableMain with fields:
tableMain.id, tableMain.status, tableMain.viewId
viewId exists to join view
Final select look like
SELECT tableMain.id
FROM tableMain
LEFT OUTER JOIN VIEW ON tableMain.viewId=view.id
and join is very slow on a VIEW.
its fast if I join directly tableA or tableB, but not when using view.
It could be fast if I use view.name in select
SELECT tableMain.id, VIEW.name
FROM tableMain
LEFT OUTER JOIN VIEW ON tableMain.viewId=view.id
Not sure why VIEW JOIN working fast if I use VIEW field in select,
and how make VIEW JOIN fast without it.
Posting plans:
Good Plan (using VIEW.name in SELECT)
SELECT tableMain.id, VIEW.name
FROM tableMain
LEFT OUTER JOIN VIEW ON tableMain.viewId=view.id
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 220K| 440M| 50 (4)| 00:00:01 |
|* 1 | HASH JOIN OUTER | | 220K| 440M| 50 (4)| 00:00:01 |
| 2 | TABLE ACCESS FULL | **tableMain** | 19796 | 1527K| 42 (0)| 00:00:01 |
| 3 | VIEW | ***VIEW*** | 1115 | 2194K| 6 (0)| 00:00:01 |
| 4 | UNION-ALL | | | | | |
| 5 | TABLE ACCESS FULL| **tableA** | 818 | 1609K| 3 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| **tableB** | 297 | 5346 | 3 (0)| 00:00:01 |
Bad Plan (no view.name in select)
SELECT tableMain.id
FROM tableMain
LEFT OUTER JOIN VIEW ON tableMain.viewId=view.id
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
| 0 | SELECT STATEMENT | | 220K| 19M| 51 (6)| 00:00:01 | | | |
| 1 | PX COORDINATOR | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10003 | 220K| 19M| 51 (6)| 00:00:01 | Q1,03 | P->S | QC (RAND) |
|* 3 | HASH JOIN RIGHT OUTER | | 220K| 19M| 51 (6)| 00:00:01 | Q1,03 | PCWP | |
| 4 | PX RECEIVE | | 1115 | 14495 | 6 (0)| 00:00:01 | Q1,03 | PCWP | |
| 5 | PX SEND HASH | :TQ10002 | 1115 | 14495 | 6 (0)| 00:00:01 | Q1,02 | P->P | HASH |
| 6 | BUFFER SORT | | 220K| 19M| | | Q1,02 | PCWP | |
| 7 | VIEW | ***VIEW*** | 1115 | 14495 | 6 (0)| 00:00:01 | Q1,02 | PCWP | |
| 8 | UNION-ALL | | | | | | Q1,02 | PCWP | |
| 9 | PX BLOCK ITERATOR | | 818 | 10634 | 3 (0)| 00:00:01 | Q1,02 | PCWC | |
| 10 | INDEX FAST FULL SCAN| ***tableA_PK*** | 818 | 10634 | 3 (0)| 00:00:01 | Q1,02 | PCWP | |
| 11 | BUFFER SORT | | | | | | Q1,02 | PCWC | |
| 12 | PX RECEIVE | | 297 | 2079 | 3 (0)| 00:00:01 | Q1,02 | PCWP | |
| 13 | PX SEND ROUND-ROBIN| :TQ10000 | 297 | 2079 | 3 (0)| 00:00:01 | | S->P | RND-ROBIN |
|* 14 | TABLE ACCESS FULL | **tableB** | 297 | 2079 | 3 (0)| 00:00:01 | | | |
| 15 | BUFFER SORT | | | | | | Q1,03 | PCWC | |
| 16 | PX RECEIVE | | 19796 | 1527K| 42 (0)| 00:00:01 | Q1,03 | PCWP | |
| 17 | PX SEND HASH | :TQ10001 | 19796 | 1527K| 42 (0)| 00:00:01 | | S->P | HASH |
| 18 | TABLE ACCESS FULL | **tableMain** | 19796 | 1527K| 42 (0)| 00:00:01 | | | |
Why so big difference?
Something is forcing parallelism. Does the view have any hints? Is there some type of plan management happening with this query? For example, is there an outline, SQL Plan Management, or profile setup only on the bad query? You may be able to find out by adding
the Note section of the explain plan. If I'm right, there will be something like this in only one of the execution plans:
Note
-----
- SQL plan baseline "SQL_PLAN_01yu884fpund494ecae5c" used FOR this statement
Also it would help to define "very slow". If the good query runs in 0.01 seconds and the bad query runs in 2 seconds, the difference may be all because of the overhead of
parallelism. But if the query was tuned for an environment with much larger data you may want to keep that the bad plan anyway - it may run better in production.

inefficient SQL plan on the partitioned Table

we are facing issue with one of query which joins between few tables.
even though there are few hundreds of records in the table the plan is going to Merge join thinking only one record in the table, please find the below plan.
when the Merge sort plan is used the query fails with temp space issue.
oracle choose Merge plan only when job loaded to newly created partition. but the rest of old partitions it is choosing Hash Join where we get results in few seconds.
for information. All the joined table has same volume.
could you please explain why this is happening?
Merg join( query hung)
-----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 712 | 36 (3)| 00:00:01 | | |
|* 1 | HASH JOIN | | 1 | 712 | 36 (3)| 00:00:01 | | |
| 2 | MERGE JOIN CARTESIAN | | 1 | 679 | 28 (0)| 00:00:01 | | |
| 3 | MERGE JOIN CARTESIAN | | 1 | 615 | 21 (0)| 00:00:01 | | |
| 4 | MERGE JOIN CARTESIAN | | 1 | 388 | 14 (0)| 00:00:01 | | |
| 5 | PARTITION RANGE SINGLE | | 1 | 105 | 7 (0)| 00:00:01 | 4 | 4 |
|* 6 | TABLE ACCESS FULL | PCA_DCM_CLNT_BSPKE_REFS_M_LND | 1 | 105 | 7 (0)| 00:00:01 | 4 | 4 |
| 7 | BUFFER SORT | | 1 | 283 | 7 (0)| 00:00:01 | | |
| 8 | PARTITION RANGE SINGLE| | 1 | 283 | 7 (0)| 00:00:01 | 4 | 4 |
|* 9 | TABLE ACCESS FULL | PCA_DCM_INDBTDNS_BLK_M_LND | 1 | 283 | 7 (0)| 00:00:01 | 4 | 4 |
| 10 | BUFFER SORT | | 1 | 227 | 14 (0)| 00:00:01 | | |
| 11 | PARTITION RANGE SINGLE | | 1 | 227 | 7 (0)| 00:00:01 | 4 | 4 |
|* 12 | TABLE ACCESS FULL | PCA_DCM_DELPHI_BLK_M_LND | 1 | 227 | 7 (0)| 00:00:01 | 4 | 4 |
| 13 | BUFFER SORT | | 1 | 64 | 21 (0)| 00:00:01 | | |
| 14 | PARTITION RANGE SINGLE | | 1 | 64 | 7 (0)| 00:00:01 | 4 | 4 |
|* 15 | TABLE ACCESS FULL | PCA_DCM_APACS_BLK_M_LND | 1 | 64 | 7 (0)| 00:00:01 | 4 | 4 |
| 16 | PARTITION RANGE SINGLE | | 1 | 33 | 7 (0)| 00:00:01 | 4 | 4 |
|* 17 | TABLE ACCESS FULL | PCA_DCM_SCORE_BLK_M_LND | 1 | 33 | 7 (0)| 00:00:01 | 4 | 4 |
-----------------------------------------------------------------------------------------------------------------------------
Hash join(Few seconds we get the results)
----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 491 | 341K| 74 (3)| 00:00:01 | | |
|* 1 | HASH JOIN | | 491 | 341K| 74 (3)| 00:00:01 | | |
| 2 | PARTITION RANGE SINGLE | | 493 | 109K| 14 (0)| 00:00:01 | 3 | 3 |
|* 3 | TABLE ACCESS FULL | PCA_DCM_DELPHI_BLK_M_LND | 493 | 109K| 14 (0)| 00:00:01 | 3 | 3 |
|* 4 | HASH JOIN | | 491 | 232K| 60 (4)| 00:00:01 | | |
|* 5 | HASH JOIN | | 492 | 99384 | 45 (3)| 00:00:01 | | |
|* 6 | HASH JOIN | | 492 | 47724 | 31 (4)| 00:00:01 | | |
| 7 | PARTITION RANGE SINGLE| | 493 | 16269 | 14 (0)| 00:00:01 | 3 | 3 |
|* 8 | TABLE ACCESS FULL | PCA_DCM_SCORE_BLK_M_LND | 493 | 16269 | 14 (0)| 00:00:01 | 3 | 3 |
| 9 | PARTITION RANGE SINGLE| | 493 | 31552 | 16 (0)| 00:00:01 | 3 | 3 |
|* 10 | TABLE ACCESS FULL | PCA_DCM_APACS_BLK_M_LND | 493 | 31552 | 16 (0)| 00:00:01 | 3 | 3 |
| 11 | PARTITION RANGE SINGLE | | 493 | 51765 | 14 (0)| 00:00:01 | 3 | 3 |
|* 12 | TABLE ACCESS FULL | PCA_DCM_CLNT_BSPKE_REFS_M_LND | 493 | 51765 | 14 (0)| 00:00:01 | 3 | 3 |
| 13 | PARTITION RANGE SINGLE | | 493 | 136K| 14 (0)| 00:00:01 | 3 | 3 |
|* 14 | TABLE ACCESS FULL | PCA_DCM_INDBTDNS_BLK_M_LND | 493 | 136K| 14 (0)| 00:00:01 | 3 | 3 |
----------------------------------------------------------------------------------------------------------------------------
Please find the query
SELECT
substr(BLK.ACC_NUM,1,14) AS ACCOUNT_NUMBER,
CASE WHEN SUBSTR(BLK.ACC_NUM,20,1) = '1' THEN 'F1'
WHEN SUBSTR(BLK.ACC_NUM,20,1) = ' ' THEN 'F1'
WHEN SUBSTR(BLK.ACC_NUM,20,1) = '0' THEN 'F1'
WHEN SUBSTR(BLK.ACC_NUM,20,1) = '2' THEN 'F2'
END FLTR,
DELPHI.ND_SPA_CII_SPA
FROM
BUR_LND.PCA_DCM_SCORE_BLK_M_LND BLK
INNER JOIN BUR_LND.PCA_DCM_CLNT_BSPKE_REFS_M_LND REFFS
ON BLK.ACC_NUM= REFFS.ACC_NUM
INNER JOIN BUR_LND.PCA_DCM_INDBTDNS_BLK_M_LND IND
ON BLK.ACC_NUM= IND.ACC_NUM
INNER JOIN BUR_LND.PCA_DCM_DELPHI_BLK_M_LND DELPHI
ON BLK.ACC_NUM= DELPHI.ACC_NUM
INNER JOIN BUR_LND.PCA_DCM_APACS_BLK_M_LND APACS
ON BLK.ACC_NUM= APACS.ACC_NUM
WHERE
BLK.EFF_DT=TO_DATE('2013-10-30','YYYY-MM-DD')
AND REFFS.EFF_DT=TO_DATE('2013-10-30','YYYY-MM-DD')
AND IND.EFF_DT=TO_DATE('2013-10-30','YYYY-MM-DD')
AND DELPHI.EFF_DT=TO_DATE('2013-10-30','YYYY-MM-DD')
AND APACS.EFF_DT=TO_DATE('2013-10-30','YYYY-MM-DD')
Thanks in advance for help.
Thanks
arkesh
The plans are bad because the new partition is missing statistics. Statistics should be updated after partition changes, ideally using incremental statistics. If that's not possible then a hint like /*+ dynamic_sampling(4) */ can help.
Statistics can be accurate, inaccurate, or missing. Missing statistics are generally not a huge problem because of dynamic sampling. With the default dynamic sampling level, 2, Oracle will automatically gather statistics if a statement includes tables without statistics.
Unfortunately for this case, Oracle only considers missing statistics per table, not per partition. (That would be a good feature request, but that won't help you right now.) With the literals in the SQL statement Oracle appears to know exactly which partition to look in. Since there are no statistics for that partition it assumes there are no rows, leading to the bad plans.
Example
Create a sample partitioned table with 1000 rows but no gathered statistics.
create table partition_test
(
a number,
b number
)
partition by range (a)
(
partition p1 values less than (2)
);
insert into partition_test select 1, level from dual connect by level <= 1000;
When there are no statistics Oracle will use dynamic sampling automatically and get a good row count. You can't see it in this simple plan, but normally this would lead to better access methods and better join operations.
explain plan for select * from partition_test where a = 1 and b <= 1000;
select * from table(dbms_xplan.display);
Plan hash value: 4097357352
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 26000 | 16 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE| | 1000 | 26000 | 16 (0)| 00:00:01 | 1 | 1 |
|* 2 | TABLE ACCESS FULL | PARTITION_TEST | 1000 | 26000 | 16 (0)| 00:00:01 | 1 | 1 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("A"=1 AND "B"<=1000)
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
Gather stats, then create a new partition with data. Although that partition has 1000 rows and no stats, Oracle does not know that and just assumes it's empty.
begin
dbms_stats.gather_table_stats(user, 'partition_test');
end;
/
alter table partition_test add partition p2 values less than (3);
insert into partition_test select 2, level from dual connect by level <= 1000;
explain plan for select * from partition_test where a = 2 and b <= 1000;
select * from table(dbms_xplan.display);
Plan hash value: 4097357352
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 9 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE| | 1 | 7 | 9 (0)| 00:00:01 | 2 | 2 |
|* 2 | TABLE ACCESS FULL | PARTITION_TEST | 1 | 7 | 9 (0)| 00:00:01 | 2 | 2 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("A"=2 AND "B"<=1000)
Explicitly requesting dynamic sampling will fix the cardinality estimates, which would likely solve your execution plan problems.
explain plan for select /*+ dynamic_sampling(4) */ * from partition_test where a = 2 and b <= 1000;
select * from table(dbms_xplan.display);
Plan hash value: 4097357352
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 7000 | 9 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE| | 1000 | 7000 | 9 (0)| 00:00:01 | 2 | 2 |
|* 2 | TABLE ACCESS FULL | PARTITION_TEST | 1000 | 7000 | 9 (0)| 00:00:01 | 2 | 2 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("A"=2 AND "B"<=1000)
Note
-----
- dynamic statistics used: dynamic sampling (level=4)

Oracle Select vs Insert/Select (11GR2)

So, I'm in the process of writing a process that will populate a table using about 10 different selects. As part of the process I've picked up a few things, however I'm facing some inconsistent behaviour at times when performing an INSERT/SELECT. When I run the select query alone, I get results back within 10 seconds (after I flush buffer cache and shared_pool), but at soon as I tag on the insert portion, takes over 10 min...
Here's what I'm doing after I clear the table... Added the "Append" hint on insert which appeared to have helped quite a bit earlier this week, but now it's back to taking longer than usual, but only when performing the insert!!
--// Disable GL_JLOG_DETAILS INDEXES
Execute immediate 'alter index IDX_GLDTL1 unusable';
Execute immediate 'alter index IDX_GLDTL2 unusable';
--// SECTION 1
INSERT /*+ APPEND PARALLEL(6) */ INTO GL_JLOG_DETAILS ( MAT_SECTION
,JLOG_KEY
,SRC_CD
,SRC_KEY
,CASE_KEY
,CASE_MBR_KEY
,UNALLOC_ACCT_CD
,FDRT_KEY
,FDRT_TR_CD
,TR_CD
,TR_REF_NO
,STAT_CD
,FD_DESC_ID
,FD_NO
,FD_TYP_CD
,BKT_NO
,RVSL_CD
,CO_CD
,BEN_RPT_TYP_CD
,DB_CR_CD
,ACCT_NO
,SUB_ACCT_NO
,JRNL_AMT
,TDTL_AMT
,RVSL_ERROR
,RVSL_SAME_DAY
,TDTL_REF_KEY)
---// SECTION 1 //--------
Select /*+ USE_NL(TMAP,TMOV,ACCT,SBNT,BNTP,FDRT) */ '1' MAT_SECTION,
JLOG.JLOG_KEY,
JLOG.SRC_CD,
TDTL.TDTL_KEY AS SRC_KEY,
TDTL.CASE_KEY,
TDTL.CASE_MBR_KEY,
TDTL.UNALLOC_ACCT_CD,
TDTL.FDRT_KEY,
FDRT.TR_CD FDRT_TR_CD,
TDTL.TR_CD,
TDTL.TR_REF_NO,
TDTL.STAT_CD,
TDTL.FD_DESC_ID,
TDTL.FD_NO,
FDDC.FD_TYP_CD,
TDTL.BKT_NO,
JLOG.RVSL_CD,
BNTP.CO_CD,
SBNT.BEN_RPT_TYP_CD,
JLOG.DB_CR_CD,
ACCT.ACCT_NO,
ACCT.SUB_ACCT_NO,
JLOG.JRNL_AMT,
ABS (TDTL.AMT) AS TDTL_AMT,
CASE
WHEN TDTL.PROC_DT < TDTL.RVSL_CYC_DT AND TDTL.ORIG_INBS_KEY IS NULL
THEN
1
ELSE
0
END RVSL_ERROR,
CASE
WHEN TDTL.PROC_DT = TDTL.RVSL_CYC_DT AND NOT TDTL.TR_CD IN ('3002','3004','1501','1502','1503','1504','1505')
THEN 1
ELSE 0
END RVSL_SAME_DAY,
TDTL.REF_KEY TDTL_REF_KEY
from GL_JOURNAL_LOGS JLOG,
Transact_Details TDTL,
FUND_DESC FDDC,
FD_RATES FDRT,
BEN_TYPES BNTP,
SYS_BEN_TYPES SBNT,
LEDGER_ACCOUNTS ACCT,
TRANS_MAP_OVRD TMOV,
TRANSACTION_MAP TMAP
WHERE JLOG.JRNL_CD = '0'
AND JLOG.SRC_CD = '2'
AND JLOG.MKEY_FD_NUM <> 0
AND NVL(JLOG.TMOV_KEY, -1) > 0
AND NVL(JLOG.ORIG_SCAT_KEY, 1) = 1
AND JLOG.Scat_key = TDTL.SCAT_KEY
AND JLOG.TR_CD = TDTL.TR_CD
AND JLOG.CASE_KEY = TDTL.CASE_KEY
AND JLOG.TR_REF_NO = TDTL.TR_REF_NO
AND JLOG.ACCT_KEY = ACCT.ACCT_KEY
AND JLOG.TMOV_KEY = TMOV.TMOV_KEY
AND NVL(TDTL.ORIG_SCAT_KEY, 1) = 1
AND TDTL.STAT_CD <> '4'
AND TDTL.FD_DESC_ID = FDDC.FD_DESC_ID
AND TDTL.FDRT_KEY = FDRT.FDRT_KEY
AND BNTP.BNTP_KEY = FDRT.BNTP_KEY
AND BNTP.SBNT_KEY (+) = SBNT.SBNT_KEY
AND TMOV.TMAP_KEY = TMAP.TMAP_KEY
AND TMOV.CO_CD = BNTP.CO_CD
AND DECODE(FDDC.MKEY_FD_NUM, NULL, TMAP.MKEY_FD_NUM, FDDC.MKEY_FD_NUM) = TMAP.MKEY_FD_NUM;
Any tips / advice would be greatly appreciated!
Explain Plan
Plan hash value: 4157721082
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 1 | 212 | 1596 (1)| 00:00:20 |
| 1 | LOAD AS SELECT | GL_JLOG_DETAILS | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1 | 212 | 1596 (1)| 00:00:20 |
| 4 | NESTED LOOPS | | 1 | 195 | 1595 (1)| 00:00:20 |
| 5 | NESTED LOOPS | | 1 | 190 | 1594 (1)| 00:00:20 |
| 6 | NESTED LOOPS | | 12 | 2172 | 1582 (1)| 00:00:19 |
|* 7 | HASH JOIN | | 12 | 2004 | 1570 (1)| 00:00:19 |
| 8 | TABLE ACCESS FULL | FUND_DESC | 168 | 1176 | 4 (0)| 00:00:01 |
| 9 | NESTED LOOPS | | | | | |
| 10 | NESTED LOOPS | | 257 | 41120 | 1566 (1)| 00:00:19 |
| 11 | NESTED LOOPS | | 257 | 22359 | 537 (0)| 00:00:07 |
| 12 | NESTED LOOPS | | 257 | 20817 | 280 (0)| 00:00:04 |
|* 13 | TABLE ACCESS BY INDEX ROWID| GL_JOURNAL_LOGS | 257 | 18504 | 23 (0)| 00:00:01 |
|* 14 | INDEX RANGE SCAN | IDX_ORIGSCATKEY | 690 | | 4 (0)| 00:00:01 |
| 15 | TABLE ACCESS BY INDEX ROWID| TRANS_MAP_OVRD | 1 | 9 | 1 (0)| 00:00:01 |
|* 16 | INDEX UNIQUE SCAN | TMOV_PK | 1 | | 0 (0)| 00:00:01 |
| 17 | TABLE ACCESS BY INDEX ROWID | TRANSACTION_MAP | 1 | 6 | 1 (0)| 00:00:01 |
|* 18 | INDEX UNIQUE SCAN | TMAP_PK | 1 | | 0 (0)| 00:00:01 |
|* 19 | INDEX RANGE SCAN | IX_AML8890 | 3 | | 3 (0)| 00:00:01 |
|* 20 | TABLE ACCESS BY INDEX ROWID | TRANSACT_DETAILS | 1 | 73 | 4 (0)| 00:00:01 |
| 21 | TABLE ACCESS BY INDEX ROWID | FD_RATES | 1 | 14 | 1 (0)| 00:00:01 |
|* 22 | INDEX UNIQUE SCAN | FDRT_PK | 1 | | 0 (0)| 00:00:01 |
|* 23 | TABLE ACCESS BY INDEX ROWID | BEN_TYPES | 1 | 9 | 1 (0)| 00:00:01 |
|* 24 | INDEX UNIQUE SCAN | BNTP_PK | 1 | | 0 (0)| 00:00:01 |
| 25 | TABLE ACCESS BY INDEX ROWID | SYS_BEN_TYPES | 1 | 5 | 1 (0)| 00:00:01 |
|* 26 | INDEX UNIQUE SCAN | SBNT_PK | 1 | | 0 (0)| 00:00:01 |
|* 27 | INDEX UNIQUE SCAN | ACCT_PK | 1 | | 0 (0)| 00:00:01 |
| 28 | TABLE ACCESS BY INDEX ROWID | LEDGER_ACCOUNTS | 1 | 17 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
7 - access("TDTL"."FD_DESC_ID"="FDDC"."FD_DESC_ID")
filter("TMAP"."MKEY_FD_NUM"=DECODE(TO_CHAR("FDDC"."MKEY_FD_NUM"),NULL,"TMAP"."MKEY_FD_NUM","
FDDC"."MKEY_FD_NUM"))
13 - filter("JLOG"."TMOV_KEY" IS NOT NULL AND "JLOG"."SRC_CD"='2' AND "JLOG"."MKEY_FD_NUM"<>0
AND NVL("TMOV_KEY",(-1))>0 AND "JLOG"."JRNL_CD"='0')
14 - access(NVL("ORIG_SCAT_KEY",1)=1)
16 - access("JLOG"."TMOV_KEY"="TMOV"."TMOV_KEY")
18 - access("TMOV"."TMAP_KEY"="TMAP"."TMAP_KEY")
19 - access("JLOG"."SCAT_KEY"="TDTL"."SCAT_KEY" AND "JLOG"."CASE_KEY"="TDTL"."CASE_KEY")
filter("TDTL"."FD_DESC_ID" IS NOT NULL AND "TDTL"."STAT_CD"<>'4' AND
"JLOG"."CASE_KEY"="TDTL"."CASE_KEY")
20 - filter("TDTL"."FDRT_KEY" IS NOT NULL AND NVL("TDTL"."ORIG_SCAT_KEY",1)=1 AND
"JLOG"."TR_CD"="TDTL"."TR_CD" AND "JLOG"."TR_REF_NO"="TDTL"."TR_REF_NO")
22 - access("TDTL"."FDRT_KEY"="FDRT"."FDRT_KEY")
23 - filter("TMOV"."CO_CD"="BNTP"."CO_CD")
24 - access("BNTP"."BNTP_KEY"="FDRT"."BNTP_KEY")
26 - access("BNTP"."SBNT_KEY"="SBNT"."SBNT_KEY")
27 - access("JLOG"."ACCT_KEY"="ACCT"."ACCT_KEY")
A couple of problems with "Direct path" load a.k.a. /*+ APPEND */.
Firstly, it isn't necessarily faster in general. It does a direct path load to disk - bypassing the buffer cache. In many cases - especially with smaller sets of data - where the direct path load to disk would be slower than a conventional path load into the cache.
Second, DPL works above the high watermark of the table which means it does not re-use the space. So unless you TRUNCATE the whole table every time, it probably isn't gonna be necessarily faster.
Third, only one seesion/user can DPL on to a table at a time. That may cause concurrency issues, here all the modifications will become serialized (like a serial circuit, rather than a parallel circuit). No insert/update/delete or merge into this table will be allowed until the transaction that direct paths, commits.
Lastly, the indexes of your table in question matters. It is pretty difficult to say without looking at the data as of what kind of index will be appropriate and on which columns. Try a combination of Local and global bitmap indexes, but be careful while chosing the columns. Making an index 'unusable' is not a good idea in general, there is a purpose why it was created and altering it almost conflicts to its immediate interests.
To start with, remove all hints, try indexing. and iff it is a reloadable table then truncate it prior to every load.

Resources