Related
I have a 118 line query similar to this:
select * from (The inner query with many joins) WHERE campus_code IN ('560','598')
The campus_code is getting generated by a function f_get_bookstore(SSBSECT_TERM_CODE,SSBSECT_CRN) AS SSBSECT_CAMP_CODE in the inner query. If I run the inner query alone its count is 18 & it is getting executed in 13 s. But if I add the WHERE campus_code IN ('560','598') then it is taking more than 2 min.
Another strange thing is, I have another query which is also like
select * from (The inner query with many joins) WHERE campus_code IN ('560','598'). Here the inner query slightly different than previous one. The inner query is fast & returning 92 rows. But here the campus_code IN filtering is fast even though it is working on 92 rows (unlike 18 rows for the previous query). Here also the campus_code is generated through the same function.
Can any body help to tune the query? Please tell what more info do you need.
This is the entire query:
SELECT *
FROM
(SELECT 'columbusstate' bk_institution_id,
'columbusstate.'
|| scbcrse_subj_code
|| '.'
|| scbcrse_crse_numb
||'.'
|| Ssbsect_Crn
|| '.'
|| ssbsect_term_code bk_section_id,
ssbsect_camp_code AS campus_code,
scbcrse_subj_code
|| '.'
|| scbcrse_crse_numb
||'.'
|| Ssbsect_Crn
|| '.'
|| ssbsect_term_code institution_section_id,
ssbsect_crn short_description,
scbcrse_title AS description,
ssbsect_crn sections_ssbsect_crn,
ssbsect_term_code sections_ssbsect_term_code,
'na' instructor_first_name,
'na' instructor_last_name,
scbcrse_subj_code
|| '.'
|| scbcrse_crse_numb rel_institution_course_id,
scbcrse_title AS rel_course_description,
scbcrse_title AS rel_course_name,
scbcrse_crse_numb AS course_short_desc,
scbcrse_crse_numb course_number,
stvterm_desc term_short_desc,
stvterm_code rel_institution_term_id,
section.ssbsect_crn,
ssbsect_ssts_code AS ssbsect_ssts_code,
'columbusstate.'
|| stvterm_code rel_bk_term_id,
'columbusstate.'
|| scbcrse_subj_code
|| '.'
|| scbcrse_crse_numb rel_bk_course_id,
ssbsect_seq_numb,
ssbsect_enrl ssbsect_enrl,
ssbsect_enrl estimated_enrl,
ssbsect_max_enrl ssbsect_max_enrl,
ssbsect_crn ssbsect_section_key,
ssbsect_crn ssbsect_section_number,
'parent' relationship,
course.scbcrse_subj_code institution_department_id ,
'DFLT' institution_division_id,
'DFLT' division_short_desc
FROM
(SELECT *
FROM
(SELECT SSBSECT_TERM_CODE,
SSBSECT_CRN,
SSBSECT_SUBJ_CODE,
SSBSECT_CRSE_NUMB,
SSBSECT_PTRM_CODE,
SSBSECT_SEQ_NUMB,
SSBSECT_SSTS_CODE,
SSBSECT_MAX_ENRL,
SSBSECT_ENRL,
SSBSECT_PRNT_IND,
f_get_bookstore(SSBSECT_TERM_CODE,SSBSECT_CRN) AS SSBSECT_CAMP_CODE
FROM SSBSECT
) sect
JOIN
(SELECT *
FROM
(SELECT * FROM saturn.stvterm WHERE STVTERM_CODE >= '201401'
)
) term
ON term.stvterm_code=sect.ssbsect_term_code
) section
JOIN
(SELECT C1.scbcrse_subj_code,
C1.scbcrse_dept_code,
C1.scbcrse_crse_numb,
C1.scbcrse_title
FROM saturn.scbcrse C1
LEFT JOIN saturn.scbcrse C2
ON (C1.scbcrse_subj_code = C2.scbcrse_subj_code
AND C1.scbcrse_crse_numb = C2.scbcrse_crse_numb
AND C1.scbcrse_eff_term < C2.scbcrse_eff_term)
WHERE c2.scbcrse_eff_term IS NULL
) Course ON Course.scbcrse_subj_code = Section.ssbsect_subj_code
AND scbcrse_crse_numb = section.ssbsect_crse_numb
AND ssbsect_ssts_code IN ('A','V','X')
LEFT JOIN
(SELECT sirasgn_term_code sirasgn_term_code,
sirasgn_crn,
MAX(spriden_pidm) spriden_pidm,
MAX(spriden_first_name) instructor_first_name,
MAX(spriden_last_name) instructor_last_name
FROM
(SELECT Pidm spriden_pidm,
Csu_Id SPRIDEN_ID,
First_Name spriden_first_name,
Last_Name spriden_last_name,
Mi spriden_mi,
External_User_Id login,
Email
FROM Csuapps.Wfollett_Person
) persons
JOIN
( SELECT * FROM saturn.sirasgn WHERE SIRASGN_PRIMARY_IND = 'Y'
) relations
ON persons.spriden_pidm=relations.sirasgn_pidm
GROUP BY sirasgn_crn,
sirasgn_term_code
) instr ON section.ssbsect_term_code=instr.sirasgn_term_code
AND section.ssbsect_crn =instr.sirasgn_crn
WHERE Course.scbcrse_subj_code = 'ACCT'
AND section.ssbsect_term_code = '201702'
)
WHERE campus_code IN ('560','1157')
Execution Plan:
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2063389120
-----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 77 | 968K| | 4364 (3)| 00:00:01 |
| 1 | SORT ORDER BY | | 77 | 968K| 624K| 4364 (3)| 00:00:01 |
|* 2 | VIEW | | 77 | 968K| | 4155 (3)| 00:00:01 |
| 3 | COUNT | | | | | | |
| 4 | VIEW | | 77 | 967K| | 4155 (3)| 00:00:01 |
| 5 | SORT ORDER BY | | 77 | 774K| | 4155 (3)| 00:00:01 |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|* 6 | FILTER | | | | | | |
|* 7 | HASH JOIN RIGHT OUTER | | 77 | 774K| | 4154 (3)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | SCBCRSE_KEY_INDEX | 83 | 1494 | | 1 (0)| 00:00:01 |
| 9 | NESTED LOOPS | | 77 | 772K| | 4153 (3)| 00:00:01 |
| 10 | NESTED LOOPS OUTER | | 23 | 229K| | 4139 (3)| 00:00:01 |
|* 11 | HASH JOIN OUTER | | 23 | 1656 | | 3783 (1)| 00:00:01 |
| 12 | NESTED LOOPS | | 23 | 1334 | | 7 (0)| 00:00:01 |
| 13 | TABLE ACCESS BY INDEX ROWID | STVTERM | 1 | 22 | | 1 (0)| 00:00:01 |
|* 14 | INDEX UNIQUE SCAN | PK_STVTERM | 1 | | | 1 (0)| 00:00:01 |
|* 15 | TABLE ACCESS BY INDEX ROWID BATCHED| SSBSECT | 23 | 828 | | 6 (0)| 00:00:01 |
|* 16 | INDEX RANGE SCAN | SSBSECT_INDEX_SUBJ | 25 | | | 1 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 17 | VIEW | | 2172 | 30408 | | 3775 (1)| 00:00:01 |
| 18 | HASH GROUP BY | | 2172 | 8537K| 16M| 3775 (1)| 00:00:01 |
|* 19 | HASH JOIN | | 4166 | 15M| | 1364 (1)| 00:00:01 |
| 20 | VIEW | VW_GBF_14 | 2312 | 18496 | | 211 (1)| 00:00:01 |
| 21 | HASH GROUP BY | | 2312 | 48552 | | 211 (1)| 00:00:01 |
|* 22 | TABLE ACCESS FULL | SIRASGN | 2312 | 48552 | | 210 (1)| 00:00:01 |
| 23 | VIEW | WFOLLETT_PERSON | 3714 | 14M| | 1153 (1)| 00:00:01 |
| 24 | SORT UNIQUE | | 3714 | 342K| | 1153 (1)| 00:00:01 |
| 25 | UNION-ALL | | | | | | |
| 26 | NESTED LOOPS | | 1 | 59 | | 2 (0)| 00:00:01 |
| 27 | NESTED LOOPS | | 1 | 59 | | 2 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|* 28 | INDEX RANGE SCAN | PK_GLBEXTR | 1 | 38 | | 1 (0)| 00:00:01 |
|* 29 | INDEX UNIQUE SCAN | PK_GOBTPAC | 1 | | | 1 (0)| 00:00:01 |
| 30 | TABLE ACCESS BY INDEX ROWID | GOBTPAC | 1 | 21 | | 1 (0)| 00:00:01 |
| 31 | NESTED LOOPS | | 1912 | 130K| | 605 (0)| 00:00:01 |
| 32 | NESTED LOOPS | | 1912 | 130K| | 605 (0)| 00:00:01 |
|* 33 | HASH JOIN | | 1912 | 93688 | | 31 (0)| 00:00:01 |
| 34 | VIEW | VW_SQ_2 | 3076 | 83052 | | 10 (0)| 00:00:01 |
| 35 | HASH GROUP BY | | 3076 | 39988 | | 10 (0)| 00:00:01 |
| 36 | INDEX FULL SCAN | PK_SIBINST | 6243 | 81159 | | 10 (0)| 00:00:01 |
|* 37 | TABLE ACCESS FULL | SIBINST | 3881 | 85382 | | 21 (0)| 00:00:01 |
|* 38 | INDEX UNIQUE SCAN | PK_GOBTPAC | 1 | | | 1 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 39 | TABLE ACCESS BY INDEX ROWID | GOBTPAC | 1 | 21 | | 1 (0)| 00:00:01 |
| 40 | NESTED LOOPS | | 1801 | 41423 | | 543 (1)| 00:00:01 |
| 41 | NESTED LOOPS | | 1801 | 41423 | | 543 (1)| 00:00:01 |
| 42 | VIEW | VW_DTP_6100A9C4 | 1801 | 3602 | | 3 (34)| 00:00:01 |
| 43 | HASH UNIQUE | | 1801 | 23413 | | 3 (34)| 00:00:01 |
|* 44 | INDEX RANGE SCAN | PK_SIRASGN | 4161 | 54093 | | 2 (0)| 00:00:01 |
|* 45 | INDEX UNIQUE SCAN | PK_GOBTPAC | 1 | | | 1 (0)| 00:00:01 |
| 46 | TABLE ACCESS BY INDEX ROWID | GOBTPAC | 1 | 21 | | 1 (0)| 00:00:01 |
| 47 | VIEW PUSHED PREDICATE | | 1 | 10165 | | 16 (25)| 00:00:01 |
|* 48 | FILTER | | | | | | |
| 49 | NESTED LOOPS | | 1 | 10164 | | 16 (25)| 00:00:01 |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 50 | NESTED LOOPS | | 1 | 79 | | 4 (0)| 00:00:01 |
|* 51 | FILTER | | | | | | |
| 52 | NESTED LOOPS OUTER | | 1 | 60 | | 3 (0)| 00:00:01 |
| 53 | NESTED LOOPS | | 1 | 42 | | 2 (0)| 00:00:01 |
| 54 | TABLE ACCESS BY INDEX ROWID | SSBSECT | 1 | 24 | | 1 (0)| 00:00:01 |
|* 55 | INDEX UNIQUE SCAN | PK_SSBSECT | 1 | | | 1 (0)| 00:00:01 |
|* 56 | INDEX RANGE SCAN | SCBCRSE_KEY_INDEX | 1 | 18 | | 1 (0)| 00:00:01 |
|* 57 | INDEX RANGE SCAN | SCBCRSE_KEY_INDEX | 15046 | 264K| | 1 (0)| 00:00:01 |
|* 58 | INDEX RANGE SCAN | PK_SIRASGN | 1 | 19 | | 1 (0)| 00:00:01 |
| 59 | VIEW | WFOLLETT_PERSON | 1 | 10085 | | 12 (34)| 00:00:01 |
| 60 | SORT UNIQUE | | 3 | 304 | | 12 (34)| 00:00:01 |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 61 | UNION ALL PUSHED PREDICATE | | | | | | |
| 62 | NESTED LOOPS | | 1 | 59 | | 2 (0)| 00:00:01 |
| 63 | TABLE ACCESS BY INDEX ROWID | GOBTPAC | 1 | 21 | | 1 (0)| 00:00:01 |
|* 64 | INDEX UNIQUE SCAN | PK_GOBTPAC | 1 | | | 1 (0)| 00:00:01 |
|* 65 | INDEX RANGE SCAN | PK_GLBEXTR | 1 | 38 | | 1 (0)| 00:00:01 |
| 66 | NESTED LOOPS | | 1 | 70 | | 3 (0)| 00:00:01 |
| 67 | NESTED LOOPS | | 1 | 70 | | 3 (0)| 00:00:01 |
| 68 | NESTED LOOPS | | 1 | 48 | | 2 (0)| 00:00:01 |
| 69 | TABLE ACCESS BY INDEX ROWID | GOBTPAC | 1 | 21 | | 1 (0)| 00:00:01 |
|* 70 | INDEX UNIQUE SCAN | PK_GOBTPAC | 1 | | | 1 (0)| 00:00:01 |
| 71 | VIEW | VW_SQ_1 | 1 | 27 | | 1 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 72 | SORT GROUP BY | | 1 | 13 | | 1 (0)| 00:00:01 |
|* 73 | INDEX RANGE SCAN | PK_SIBINST | 2 | 26 | | 1 (0)| 00:00:01 |
|* 74 | INDEX UNIQUE SCAN | PK_SIBINST | 1 | | | 1 (0)| 00:00:01 |
|* 75 | TABLE ACCESS BY INDEX ROWID | SIBINST | 1 | 22 | | 1 (0)| 00:00:01 |
| 76 | NESTED LOOPS | | 1 | 23 | | 4 (25)| 00:00:01 |
| 77 | TABLE ACCESS BY INDEX ROWID | GOBTPAC | 1 | 21 | | 1 (0)| 00:00:01 |
|* 78 | INDEX UNIQUE SCAN | PK_GOBTPAC | 1 | | | 1 (0)| 00:00:01 |
|* 79 | VIEW | VW_DTP_32C68FB6 | 1 | 2 | | 3 (34)| 00:00:01 |
| 80 | SORT UNIQUE | | 2 | 26 | | 3 (34)| 00:00:01 |
|* 81 | INDEX RANGE SCAN | PK_SIRASGN | 2 | 26 | | 2 (0)| 00:00:01 |
| 82 | TABLE ACCESS BY INDEX ROWID BATCHED | SCBCRSE | 3 | 126 | | 1 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|* 83 | INDEX RANGE SCAN | SCBCRSE_KEY_INDEX | 1 | | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("from$_subquery$_002"."CAMPUS_CODE"='1157' OR "from$_subquery$_002"."CAMPUS_CODE"='560') AND "R">=1
AND "R"<=1500)
6 - filter("C2"."SCBCRSE_EFF_TERM" IS NULL)
7 - access("C1"."SCBCRSE_SUBJ_CODE"="C2"."SCBCRSE_SUBJ_CODE"(+) AND
"C1"."SCBCRSE_CRSE_NUMB"="C2"."SCBCRSE_CRSE_NUMB"(+))
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
filter("C1"."SCBCRSE_EFF_TERM"<"C2"."SCBCRSE_EFF_TERM"(+))
8 - access("C2"."SCBCRSE_SUBJ_CODE"(+)='ACCT')
11 - access("SSBSECT_TERM_CODE"="INSTR"."SIRASGN_TERM_CODE"(+) AND "SSBSECT_CRN"="INSTR"."SIRASGN_CRN"(+))
14 - access("STVTERM"."STVTERM_CODE"='201702')
15 - filter("SSBSECT_SSTS_CODE"='A' OR "SSBSECT_SSTS_CODE"='V' OR "SSBSECT_SSTS_CODE"='X')
16 - access("SSBSECT_SUBJ_CODE"='ACCT' AND "SSBSECT_TERM_CODE"='201702')
filter("SSBSECT_TERM_CODE"='201702')
19 - access("PIDM"="ITEM_1")
22 - filter("SIRASGN"."SIRASGN_TERM_CODE"='201702' AND "SIRASGN_PRIMARY_IND"='Y')
28 - access("GLBEXTR_APPLICATION"='STUDENT' AND "GLBEXTR_SELECTION"='CURR_ENRL' AND "GLBEXTR_CREATOR_ID"='GSHOUL')
29 - access("GOBTPAC_PIDM"=TO_NUMBER("GLBEXTR_KEY"))
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
33 - access("A"."SIBINST_TERM_CODE_EFF"="MAX(B.SIBINST_TERM_CODE_EFF)" AND "ITEM_2"="A"."SIBINST_PIDM")
37 - filter("A"."SIBINST_FCST_CODE"='AC' AND "A"."SIBINST_FCTG_CODE"<>'EMPL')
38 - access("A"."SIBINST_PIDM"="GOBTPAC_PIDM")
44 - access("SIRASGN_TERM_CODE">="TOOLS"."GETTERMCODE"(NULL,SYSDATE#!))
45 - access("ITEM_1"="GOBTPAC_PIDM")
48 - filter("SSBSECT_TERM_CODE">='201602' AND "SSBSECT_TERM_CODE"='201702')
51 - filter("C2"."SCBCRSE_EFF_TERM" IS NULL)
55 - access("SSBSECT_TERM_CODE"="SSBSECT_TERM_CODE" AND "SSBSECT_CRN"="SSBSECT_CRN")
filter("SSBSECT_TERM_CODE"='201702')
56 - access("SSBSECT_SUBJ_CODE"="C1"."SCBCRSE_SUBJ_CODE" AND "SSBSECT_CRSE_NUMB"="C1"."SCBCRSE_CRSE_NUMB")
57 - access("C1"."SCBCRSE_SUBJ_CODE"="C2"."SCBCRSE_SUBJ_CODE"(+) AND
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
"C1"."SCBCRSE_CRSE_NUMB"="C2"."SCBCRSE_CRSE_NUMB"(+) AND "C1"."SCBCRSE_EFF_TERM"<"C2"."SCBCRSE_EFF_TERM"(+) AND
"C2"."SCBCRSE_EFF_TERM"(+) IS NOT NULL)
58 - access("REL"."SIRASGN_TERM_CODE"="SSBSECT_TERM_CODE" AND "REL"."SIRASGN_CRN"="SSBSECT_CRN")
filter("REL"."SIRASGN_TERM_CODE"="SSBSECT_TERM_CODE" AND "REL"."SIRASGN_TERM_CODE"='201702' AND
"REL"."SIRASGN_CRN"="SSBSECT_CRN")
64 - access("GOBTPAC_PIDM"="REL"."SIRASGN_PIDM")
65 - access("GLBEXTR_APPLICATION"='STUDENT' AND "GLBEXTR_SELECTION"='CURR_ENRL' AND "GLBEXTR_CREATOR_ID"='GSHOUL')
filter(TO_NUMBER("GLBEXTR_KEY")="REL"."SIRASGN_PIDM" AND "GOBTPAC_PIDM"=TO_NUMBER("GLBEXTR_KEY"))
70 - access("GOBTPAC_PIDM"="REL"."SIRASGN_PIDM")
73 - access("B"."SIBINST_PIDM"="REL"."SIRASGN_PIDM")
74 - access("A"."SIBINST_PIDM"="REL"."SIRASGN_PIDM" AND "A"."SIBINST_TERM_CODE_EFF"="MAX(B.SIBINST_TERM_CODE_EFF)")
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
filter("A"."SIBINST_PIDM"="GOBTPAC_PIDM" AND "ITEM_1"="A"."SIBINST_PIDM")
75 - filter("A"."SIBINST_FCST_CODE"='AC' AND "A"."SIBINST_FCTG_CODE"<>'EMPL')
78 - access("GOBTPAC_PIDM"="REL"."SIRASGN_PIDM")
79 - filter("ITEM_1"="GOBTPAC_PIDM")
81 - access("SIRASGN_TERM_CODE">="TOOLS"."GETTERMCODE"(NULL,SYSDATE#!) AND "SIRASGN_PIDM"="REL"."SIRASGN_PIDM")
filter("SIRASGN_PIDM"="REL"."SIRASGN_PIDM")
83 - access("C1"."SCBCRSE_SUBJ_CODE"='ACCT' AND "C1"."SCBCRSE_CRSE_NUMB"="SSBSECT_CRSE_NUMB")
139 rows selected.
The performance of your query depends on the details, that we don't know, and on the joins. In general, if the joins result in a huge set of rows, that will eat a lot of cpu and ram. So try to add where clauses that minimize the number of rows in the joins.
Said in an other way, just imagine you are the cpu. Go through your query and try to estimate the number of rows that you need to process, and how you could limit that number of rows.
Are you using an index on crucial tables to speed up selects? Are you modifying the search column used in an index, so that the index does not work? Stuff like this is very important.
Good luck!
update:
The plan shows to me that most cpu is burned for the outermost query, something that you already noticed in the timing measurements. My strategy would be to try to move the outermost where-clause towards the inner queries. If the database engine can apply the where-clause in an earlier phase, the query will need less memory and cpu.
Now it is not possible to see to which table each output field belongs. If I where you I would give EVERY table in each FROM an alias.
For the sake of testing performance, and for posting here, you could simplify the lists of output fields. Leave out fields that are not required by outer queries, or use asterisk. Also, try to present a perfectly pretty printed query.
Apart from the Plan, could you get some info on memory usage? With the outer query, perhaps it needs to get a lot of data in memory before outputting results. Just imagine if this results in exhausting ram and needs swap, that will cost a lot of time. Memory use may also come from holding a lock on possibly big tables, so do not get fooled by your 'just 18 rows' of output.
OK, I'm going to put this in a "answer", since comments seem to be ignored.
The only way to know what is going on and how to fix your problem is to get the execution plan. Otherwise every answer is a guess. You may get lucky that a guess works, but then you will never know why. So please help us to help you.
To get an execution plan (for both the queries with and without the WHERE clause), run this after your query...
set lines 500
set pages 10000
select * from table( dbms_xplan.display_cursor( null, null, 'TYPICAL' ));
Is campus_code an integer? If it is, try removing the apostrophes, like this:
WHERE campus_code IN (560,598)
Otherwise, each value gets converted before being compared, and that can take a lot of time.
Hope this helps.
What you observe is by 99% a merging predicates in an inline view.
This works typically fine and helps performance as illustrated on an example below.
Sample Data
create table T1 as
select 'xxxx'||rownum crampus_code from dual connect by level <= 100000;
create index idx1 on t1(crampus_code);
This query can take a substantial time on a large table as the whole table must be accessed.
select crampus_code, count(*) from T1 group by crampus_code;
A following query using the previous query as a subquery can be very effective. How?
The WHERE clause in MERGE in the subquery and only the data for the two keys are accessed (index) and aggregated.
with agg as
(
select
a.crampus_code, count(*)
from
T1 a
group by
crampus_code
)
select *
from agg
where crampus_code in ('xxxx42','xxxx399');
The proof is in the execution plan (that you don't provided)
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 48 | 3 (0)| 00:00:01 |
| 1 | SORT GROUP BY NOSORT| | 2 | 48 | 3 (0)| 00:00:01 |
| 2 | INLIST ITERATOR | | | | | |
|* 3 | INDEX RANGE SCAN | IDX1 | 2 | 48 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("A"."CRAMPUS_CODE"='xxxx399' OR
"A"."CRAMPUS_CODE"='xxxx42')
You see the table is accesse via index for only the two keys.
Your Case
This is only speculation but IMO during the merge predicate phase you get a much worst execution plan that without it.
This explain the increase in elapsed time by addin the WHERE clause.
Only if you show both plans (subquery and the query with WHERE) you can be sure...
Workaround
So what can you do if the predicate merge destroys the performance?
Simple turn it off. Unfortunately you must use an undocumented hint MATERIALIZE, that set up the result of the query as temporary table
and the WHERE predicate will be applied on it.
This should work for your 18 rows. I'm not sure, but thing you must rewrite query using the subquery factoring (WITH).
with agg as
(select /*+ MATERIALIZE */ a.crampus_code, count(*) from T1 a group by crampus_code)
select * from agg
where crampus_code in ('xxxx42','xxxx399');
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 97325 | 3516K| 94 (5)| 00:00:02 |
| 1 | TEMP TABLE TRANSFORMATION | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D673A_CE046F62 | | | | |
| 3 | HASH GROUP BY | | 97325 | 2281K| 41 (10)| 00:00:01 |
| 4 | TABLE ACCESS FULL | T1 | 97325 | 2281K| 37 (0)| 00:00:01 |
|* 5 | VIEW | | 97325 | 3516K| 54 (2)| 00:00:01 |
| 6 | TABLE ACCESS FULL | SYS_TEMP_0FD9D673A_CE046F62 | 97325 | 2281K| 54 (2)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - filter("CRAMPUS_CODE"='xxxx399' OR "CRAMPUS_CODE"='xxxx42')
I got task to improve existing code / query from my company,
Database version
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
"CORE 10.2.0.4.0 Production"
TNS for IBM/AIX RISC System/6000: Version 10.2.0.4.0 - Productio
NLSRTL Version 10.2.0.4.0 - Production
Here's the problem- when below code is executed, the time taken to finish the job is more than 4 hours, something around 7 to 8 hours.
395 row data within 3 hours 37 minutes
SELECT DISTINCT GROUP_DIST_NUMBER, BEGIN_DATE, PRICE_DROP_DATE
FROM (SELECT DISTINCT
G.GROUP_DIST_NUMBER,
TO_DATE (:B2, 'DD-MON-YYYY') BEGIN_DATE,
TO_DATE (:B2, 'DD-MON-YYYY') PRICE_DROP_DATE
FROM POS_DISTI_GROUP G,
POS_CUST_XREF M,
S_CPT_SEQ_NO C,
PP_STD_PRICE P,
S_CPT_AUDIT A,
RPT_PRODUCT_VALUE_LEVEL L
WHERE G.END_DATE > TO_DATE (:B2, 'DD-MON-YYYY')
AND G.GROUP_DIST_NUMBER = M.DIST_NUMBER
AND M.SG_BILL_TO_CUST_NO = A.BILL_TO_CUST_NO
AND A.START_DATE <= TO_DATE (:B2, 'DD-MON-YYYY')
AND A.END_DATE >= TO_DATE (:B2, 'DD-MON-YYYY')
AND L.PROD_VALUE = P.PROD_VALUE
AND L.PROD_LEVEL = P.PROD_LEVEL
AND C.CPT_PRICE_CODE IN
(SELECT /*+ PRECOMPUTE_SUBQUERY */
DISTINCT C1.CPT_PRICE_CODE
FROM PP_STD_PRICE P1,
S_CPT_PRICE_CODE C1,
S_CPT_SEQ_NO S1
WHERE P1.STDP_ID = :B1
AND C1.CPT_PRICE_CAT LIKE 'NB%'
AND C1.CPT_PRICE_CODE = S1.CPT_PRICE_CODE
AND S1.PRICE_PROTECTABLE = 'Y')
AND C.CPT_PRICE_CODE = P.CUST_PRICE_TYPE
AND P.STDP_ID = :B1
AND A.CUST_PRICE_TYPE = C.CPT_BILL_CODE
AND M.ACTIVE_IND != 'N'
AND (M.CATEGORY_TYPE LIKE 'DIRECT%' OR M.INDIRECT_DISTI = 'Y')
AND TRUNC (M.ARCHIVE_DATE) > TRUNC (SYSDATE)
UNION
SELECT G.GROUP_DIST_NUMBER,
P.BEGIN_DATE,
MIN (INVT.PRICE_DROP_DATE) PRICE_DROP_DATE
FROM POS_DISTI_GROUP G,
POS_CUST_XREF M,
PP_DEBIT_AUTHORIZATION P,
RPT_PRODUCT_VALUE_LEVEL L,
POS_PP_INVENTORY INVT
WHERE G.END_DATE > TO_DATE (:B2, 'DD-MON-YYYY')
AND G.GROUP_DIST_NUMBER = M.DIST_NUMBER
AND M.ACTIVE_IND != 'N'
AND (M.CATEGORY_TYPE LIKE 'DIRECT%' OR M.INDIRECT_DISTI = 'Y')
AND G.DIST_NUMBER = P.DIST_NUMBER
AND L.PROD_VALUE = P.PROD_VALUE
AND L.PROD_LEVEL = P.PROD_LEVEL
AND P.BEGIN_DATE >= TO_DATE (:B2, 'DD-MON-YYYY') - 6
AND P.BEGIN_DATE <= TO_DATE (:B2, 'DD-MON-YYYY')
AND INVT.DIST_NUMBER = G.GROUP_DIST_NUMBER
AND INVT.STMODEL = L.MOD_DESC
AND INVT.PPCF_SHOW_DATE = P.BEGIN_DATE
AND P.PRICE_TYPE = 'I'
AND ( P.POS_PROCESSED_FLAG IS NULL
OR P.POS_PROCESSED_FLAG != 'C')
AND P.POS_PP_FLAG = 'Y'
AND TRUNC (M.ARCHIVE_DATE) > TRUNC (SYSDATE)
GROUP BY G.GROUP_DIST_NUMBER, P.BEGIN_DATE)
ORDER BY GROUP_DIST_NUMBER;
I have no idea how to tune this query statement to improve the performance and make it execute faster
here the EXPLAIN PLAN
--------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 101 | 2525 | | 24156 (10)| | |
| 1 | SORT ORDER BY | | 101 | 2525 | | 24156 (10)| | |
| 2 | VIEW | | 101 | 2525 | | 24155 (10)| | |
| 3 | SORT UNIQUE | | 101 | 17691 | | 24155 (75)| | |
| 4 | UNION-ALL | | | | | | | |
|* 5 | HASH JOIN | | 10M| 1680M| | 6446 (5)| | |
|* 6 | TABLE ACCESS FULL | S_CPT_SEQ_NO | 651 | 5208 | | 5 (0)| | |
|* 7 | HASH JOIN | | 2383K| 379M| | 6318 (3)| | |
|* 8 | TABLE ACCESS FULL | POS_DISTI_GROUP | 100 | 1800 | | 5 (0)| | |
|* 9 | HASH JOIN | | 2396K| 340M| 4320K| 6283 (3)| | |
| 10 | VIEW | RPT_PRODUCT_VALUE_LEVEL | 138K| 2697K| | 1905 (3)| | |
| 11 | UNION-ALL | | | | | | | |
|* 12 | HASH JOIN RIGHT OUTER | | 13965 | 627K| | 91 (5)| | |
| 13 | INDEX FULL SCAN | PK_SEAEGO_PRODUCT_HIERARCHY | 298 | 4172 | | 1 (0)| | |
|* 14 | HASH JOIN RIGHT OUTER | | 13965 | 436K| | 89 (4)| | |
| 15 | INDEX FULL SCAN | PK_S_CAP_GROUP | 2 | 8 | | 1 (0)| | |
| 16 | TABLE ACCESS FULL | SMA_STMODEL | 13965 | 381K| | 87 (3)| | |
|* 17 | HASH JOIN RIGHT OUTER | | 14175 | 1065K| | 158 (5)| | |
| 18 | INDEX FAST FULL SCAN | PK_S_FAMILY | 1366 | 5464 | | 2 (0)| | |
|* 19 | HASH JOIN RIGHT OUTER | | 14175 | 1010K| | 156 (5)| | |
| 20 | INDEX FULL SCAN | PK_F_MODPRODMGR | 22 | 88 | | 1 (0)| | |
|* 21 | HASH JOIN | | 14175 | 955K| | 154 (4)| | |
| 22 | TABLE ACCESS FULL | SMA_PRODUCTMODEL | 14132 | 317K| | 62 (2)| | |
|* 23 | HASH JOIN RIGHT OUTER | | 13965 | 627K| | 91 (5)| | |
| 24 | INDEX FULL SCAN | PK_SEAEGO_PRODUCT_HIERARCHY | 298 | 4172 | | 1 (0)| | |
|* 25 | HASH JOIN RIGHT OUTER | | 13965 | 436K| | 89 (4)| | |
| 26 | INDEX FULL SCAN | PK_S_CAP_GROUP | 2 | 8 | | 1 (0)| | |
| 27 | TABLE ACCESS FULL | SMA_STMODEL | 13965 | 381K| | 87 (3)| | |
| 28 | MAT_VIEW ACCESS FULL | RPT_PROD_MV | 109K| 1288K| | 1656 (3)| | |
|* 29 | HASH JOIN | | 141K| 17M| | 3191 (3)| | |
|* 30 | INDEX RANGE SCAN | UK_PP_STD_PRICE_STDP_ID | 4128 | 108K| | 23 (0)| | |
|* 31 | HASH JOIN | | 5341 | 532K| | 3165 (3)| | |
|* 32 | TABLE ACCESS FULL | POS_CUST_XREF | 54 | 2268 | | 25 (4)| | |
|* 33 | HASH JOIN | | 193K| 11M| | 3137 (3)| | |
|* 34 | TABLE ACCESS FULL | S_CPT_AUDIT | 68 | 2108 | | 76 (4)| | |
|* 35 | HASH JOIN | | 745K| 20M| | 3052 (2)| | |
| 36 | TABLE ACCESS FULL | S_CPT_SEQ_NO | 1301 | 16913 | | 5 (0)| | |
| 37 | MERGE JOIN CARTESIAN | | 88205 | 1378K| | 3037 (2)| | |
|* 38 | INDEX RANGE SCAN | UK_PP_STD_PRICE_STDP_ID | 4128 | 20640 | | 23 (0)| | |
| 39 | BUFFER SORT | | 21 | 231 | | 3014 (2)| | |
|* 40 | TABLE ACCESS FULL | S_CPT_PRICE_CODE | 21 | 231 | | 1 (0)| | |
| 41 | HASH GROUP BY | | 1 | 191 | | 16421 (5)| | |
|* 42 | FILTER | | | | | | | |
| 43 | NESTED LOOPS | | 1 | 191 | | 16419 (5)| | |
|* 44 | HASH JOIN | | 7 | 1176 | | 16370 (5)| | |
|* 45 | HASH JOIN | | 74 | 8584 | | 4790 (3)| | |
|* 46 | HASH JOIN | | 60 | 3780 | | 31 (7)| | |
|* 47 | TABLE ACCESS FULL | POS_CUST_XREF | 60 | 2100 | | 25 (4)| | |
|* 48 | TABLE ACCESS FULL | POS_DISTI_GROUP | 100 | 2800 | | 5 (0)| | |
|* 49 | TABLE ACCESS FULL | PP_DEBIT_AUTHORIZATION | 345 | 18285 | | 4759 (3)| | |
| 50 | PARTITION RANGE ALL | | 18192 | 923K| | 11579 (6)| 1 | 33 |
|* 51 | INDEX FAST FULL SCAN | POS_PP_INVENTORY_PK | 18192 | 923K| | 11579 (6)| 1 | 33 |
|* 52 | VIEW | RPT_PRODUCT_VALUE_LEVEL | 1 | 23 | | 7 (0)| | |
| 53 | UNION ALL PUSHED PREDICATE | | | | | | | |
|* 54 | FILTER | | | | | | | |
| 55 | NESTED LOOPS OUTER | | 1 | 46 | | 2 (0)| | |
| 56 | NESTED LOOPS OUTER | | 1 | 42 | | 2 (0)| | |
| 57 | TABLE ACCESS BY INDEX ROWID | SMA_STMODEL | 1 | 28 | | 2 (0)| | |
|* 58 | INDEX UNIQUE SCAN | PK_SMA_STMODEL | 1 | | | 1 (0)| | |
|* 59 | INDEX UNIQUE SCAN | PK_SEAEGO_PRODUCT_HIERARCHY | 298 | 4172 | | 0 (0)| | |
|* 60 | INDEX UNIQUE SCAN | PK_S_CAP_GROUP | 2 | 8 | | 0 (0)| | |
| 61 | NESTED LOOPS OUTER | | 1 | 77 | | 3 (0)| | |
| 62 | NESTED LOOPS OUTER | | 1 | 73 | | 3 (0)| | |
| 63 | NESTED LOOPS OUTER | | 1 | 69 | | 3 (0)| | |
| 64 | NESTED LOOPS OUTER | | 1 | 65 | | 3 (0)| | |
| 65 | NESTED LOOPS | | 1 | 51 | | 3 (0)| | |
|* 66 | TABLE ACCESS BY INDEX ROWID| SMA_PRODUCTMODEL | 1 | 23 | | 2 (0)| | |
|* 67 | INDEX UNIQUE SCAN | PK_SMA_PRODUCTMODEL | 1 | | | 1 (0)| | |
| 68 | TABLE ACCESS BY INDEX ROWID| SMA_STMODEL | 1 | 28 | | 1 (0)| | |
|* 69 | INDEX UNIQUE SCAN | PK_SMA_STMODEL | 1 | | | 0 (0)| | |
|* 70 | INDEX UNIQUE SCAN | PK_SEAEGO_PRODUCT_HIERARCHY | 298 | 4172 | | 0 (0)| | |
|* 71 | INDEX UNIQUE SCAN | PK_S_FAMILY | 1366 | 5464 | | 0 (0)| | |
|* 72 | INDEX UNIQUE SCAN | PK_S_CAP_GROUP | 2 | 8 | | 0 (0)| | |
|* 73 | INDEX UNIQUE SCAN | PK_F_MODPRODMGR | 22 | 88 | | 0 (0)| | |
|* 74 | MAT_VIEW ACCESS BY INDEX ROWID | RPT_PROD_MV | 1 | 24 | | 2 (0)| | |
|* 75 | INDEX UNIQUE SCAN | IDX_RPT_PROD_MV_PROD_NO | 1 | | | 1 (0)| | |
--------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("C1"."CPT_PRICE_CODE"="S1"."CPT_PRICE_CODE")
6 - filter("S1"."PRICE_PROTECTABLE"='Y')
7 - access("G"."GROUP_DIST_NUMBER"="M"."DIST_NUMBER")
8 - filter("G"."END_DATE">TO_DATE(:B2,'DD-MON-YYYY'))
9 - access("L"."PROD_VALUE"="P"."PROD_VALUE" AND "L"."PROD_LEVEL"="P"."PROD_LEVEL")
12 - access("ST"."MARKETING_NAME"="PH"."MARKETING_NAME"(+))
14 - access("ST"."MOD_CAPACITY_FORMATTED"="SCG"."MOD_CAPACITY_FORMATTED"(+))
17 - access("SF"."FAMILY"(+)=SUBSTRB("PM"."MODEL",1,3))
19 - access("PM"."DESIGN_APPLICATION"="DA"."DESIGN_APPLICATION"(+))
21 - access("PM"."MOD_DESC"="ST"."MOD_DESC")
23 - access("ST"."MARKETING_NAME"="PH"."MARKETING_NAME"(+))
25 - access("ST"."MOD_CAPACITY_FORMATTED"="SCG"."MOD_CAPACITY_FORMATTED"(+))
29 - access("C"."CPT_PRICE_CODE"="P"."CUST_PRICE_TYPE")
30 - access("P"."STDP_ID"=TO_NUMBER(:B1))
31 - access("M"."SG_BILL_TO_CUST_NO"="A"."BILL_TO_CUST_NO")
32 - filter("M"."SG_BILL_TO_CUST_NO" IS NOT NULL AND ("M"."INDIRECT_DISTI"='Y' OR "M"."CATEGORY_TYPE" LIKE 'DIRECT%') AND
"M"."ACTIVE_IND"<>'N' AND TRUNC(INTERNAL_FUNCTION("M"."ARCHIVE_DATE"))>TRUNC(SYSDATE#!))
33 - access("A"."CUST_PRICE_TYPE"="C"."CPT_BILL_CODE")
34 - filter("A"."START_DATE"<=TO_DATE(:B2,'DD-MON-YYYY') AND "A"."END_DATE">=TO_DATE(:B2,'DD-MON-YYYY'))
35 - access("C"."CPT_PRICE_CODE"="C1"."CPT_PRICE_CODE")
38 - access("P1"."STDP_ID"=TO_NUMBER(:B1))
40 - filter("C1"."CPT_PRICE_CAT" LIKE 'NB%')
42 - filter(TO_DATE(:B2,'DD-MON-YYYY')-6<=TO_DATE(:B2,'DD-MON-YYYY'))
44 - access("INVT"."DIST_NUMBER"="G"."GROUP_DIST_NUMBER" AND "INVT"."PPCF_SHOW_DATE"="P"."BEGIN_DATE")
45 - access("G"."DIST_NUMBER"="P"."DIST_NUMBER")
46 - access("G"."GROUP_DIST_NUMBER"="M"."DIST_NUMBER")
47 - filter(("M"."INDIRECT_DISTI"='Y' OR "M"."CATEGORY_TYPE" LIKE 'DIRECT%') AND "M"."ACTIVE_IND"<>'N' AND
TRUNC(INTERNAL_FUNCTION("M"."ARCHIVE_DATE"))>TRUNC(SYSDATE#!))
48 - filter("G"."END_DATE">TO_DATE(:B2,'DD-MON-YYYY'))
49 - filter("P"."PRICE_TYPE"='I' AND "P"."POS_PP_FLAG"='Y' AND ("P"."POS_PROCESSED_FLAG"<>'C' OR "P"."POS_PROCESSED_FLAG"
IS NULL) AND "P"."BEGIN_DATE"<=TO_DATE(:B2,'DD-MON-YYYY') AND "P"."BEGIN_DATE">=TO_DATE(:B2,'DD-MON-YYYY')-6)
51 - filter("INVT"."PPCF_SHOW_DATE"<=TO_DATE(:B2,'DD-MON-YYYY') AND "INVT"."PPCF_SHOW_DATE">=TO_DATE(:B2,'DD-MON-YYYY')-6)
52 - filter("L"."PROD_LEVEL"="P"."PROD_LEVEL")
54 - filter("P"."PROD_VALUE"="INVT"."STMODEL")
58 - access("ST"."MOD_DESC"="P"."PROD_VALUE")
59 - access("ST"."MARKETING_NAME"="PH"."MARKETING_NAME"(+))
60 - access("ST"."MOD_CAPACITY_FORMATTED"="SCG"."MOD_CAPACITY_FORMATTED"(+))
66 - filter("PM"."MOD_DESC"="INVT"."STMODEL")
67 - access("PM"."MODEL"="P"."PROD_VALUE")
69 - access("ST"."MOD_DESC"="INVT"."STMODEL")
70 - access("ST"."MARKETING_NAME"="PH"."MARKETING_NAME"(+))
71 - access("SF"."FAMILY"(+)=SUBSTRB("PM"."MODEL",1,3))
72 - access("ST"."MOD_CAPACITY_FORMATTED"="SCG"."MOD_CAPACITY_FORMATTED"(+))
73 - access("PM"."DESIGN_APPLICATION"="DA"."DESIGN_APPLICATION"(+))
74 - filter("MOD_DESC"="INVT"."STMODEL")
75 - access("PROD_NO"="P"."PROD_VALUE")
Note
-----
- 'PLAN_TABLE' is old version
and the statistic of rows count for table
TABLE_Name NUM_ROWS
----------- ---------
POS_DISTI_GROUP 2009
POS_CUST_XREF 2801
S_CPT_SEQ_NO 1301
PP_STD_PRICE 2658450
S_CPT_AUDIT 27200
PP_DEBIT_AUTHORIZATION 1199420
POS_PP_INVENTORY 7276850
PP_STD_PRICE 2658450
S_CPT_PRICE_CODE 192
S_CPT_SEQ_NO 1301
SMA_STMODEL 13965
RPT_PROD_MV 109980
create table statement. CLICK HERE
Table Description. CLICK HERE
Retrieve EXPLAIN PLAN with rerun gather_plan_statistics as #jonearles suggest. CLICK HERE
*link from google doc
The problem
Aggregation is happening too late in the execution plan. Plan IDs 4 and 5 generate 13 billion rows and account for 95% of the execution time. Oracle incorrectly believes the number of rows will be smaller, and that earlier aggregations should be merged together.
Plan IDs 6 through 40 represent the first half of the inline view, before the UNION. That part of the query has two DISTINCTs, yet there are no types of aggregation operations for that part of the execution plan. Oracle incorrectly thinks it's better to join everything first and perform one SORT UNIQUE, instead of performing multiple SORT UNIQUE or HASH GROUP BY and combining the results.
Reproduce the problem
Fully reproducing this problem without a full export is almost impossible. Even though it's only a moderately complicated SQL statement there are thousands of variables involved. The code below only demonstrates how Oracle can incorrectly merge aggregation operations.
First, create two simple tables. Each has 100K rows. TEST1 has numbers from 1 to 100000. TEST2 contains 100000 rows, but only one distinct number. To artificially make a bad plan, statistics are gathered too soon on TEST2. The optimizer thinks that TEST2 only has one row but it really has 100000.
drop table test1 purge;
drop table test2 purge;
create table test1(a number);
create table test2(a number);
insert into test1 select level from dual connect by level <= 100000;
insert into test2 values (1);
commit;
begin
dbms_stats.gather_table_stats(user, 'test1');
dbms_stats.gather_table_stats(user, 'test2');
end;
/
insert into test2 select 1 from dual connect by level <= 100000;
commit;
The sample query below retrieves all distinct TEST1.A where A is also in distinct TEST2.A.
By default, using the artificially bad statistics, Oracle joins the tables first and then performs the HASH GROUP BY and HASH UNIQUE. This is a bad plan, it joins all
100K values from TEST2. It would be better to perform the HASH GROUP BY first and then only join 1 row from that table.
explain plan for
select distinct a from test1 where a in (select a from test2 group by a);
select * from table(dbms_xplan.display(format => 'outline'));
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 79 (2)| 00:00:01 |
| 1 | HASH UNIQUE | | 1 | 8 | 79 (2)| 00:00:01 |
| 2 | HASH GROUP BY | | 1 | 8 | 79 (2)| 00:00:01 |
|* 3 | HASH JOIN | | 1 | 8 | 79 (2)| 00:00:01 |
| 4 | TABLE ACCESS FULL| TEST2 | 1 | 3 | 3 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL| TEST1 | 100K| 488K| 76 (2)| 00:00:01 |
------------------------------------------------------------------------------
Potential Solution #1: Hints
Unfortunately there are no official hints to control when and where sorting and grouping happen. By playing around with the outline format option I was able to find a few potentially helpful hints: USE_HASH_AGGREGATION, OUTLINE_LEAF, and PLACE_DISTINCT. (These hints are really tricky - the reason I used a group by instead of another distinct in my sample is because I had so much trouble with the PLACE_DISTINCT hint!)
Using these undocumented hints can build a better plan. The results from TEST2 go through a HASH GROUP BY right away, as they should. This is similar to the plan that would be produced if the statistics were accurate.
explain plan for
select /*+ USE_HASH_AGGREGATION(#"SEL$5DA710D3") OUTLINE_LEAF(#"SEL$683B0107") */
distinct a from test1 where a in (select a from test2 group by a);
select * from table(dbms_xplan.display(format => 'outline alias'));
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 79 (2)| 00:00:01 |
| 1 | HASH UNIQUE | | 1 | 8 | 79 (2)| 00:00:01 |
|* 2 | HASH JOIN SEMI | | 1 | 8 | 79 (2)| 00:00:01 |
| 3 | VIEW | VW_NSO_1 | 1 | 3 | 3 (0)| 00:00:01 |
| 4 | HASH GROUP BY | | 1 | 3 | 3 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL| TEST2 | 1 | 3 | 3 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL | TEST1 | 100K| 488K| 76 (2)| 00:00:01 |
----------------------------------------------------------------------------------
Potential Solution #2: Force a plan with ROWNUM.
A much simpler and safer version of the above is to use ROWNUM. ROWNUM is a pseudocolumn that represents the order of the rows returned. When there is a ROWNUM Oracle cannot move the distinct and group by because it would affect that order.
Unfortunately, this trick requires extra code and generates extra steps in the plan. Those extra steps are mostly just passing data through and shouldn't slow things done much.
explain plan for
select distinct a from test1 where a in
(
--Extra level only because we only want to project one column.
--It's syntactically required, but the optimizer throws out this inline view.
select a
from
(
--The ROWNUM forces everything in this inline view to happen separately.
select a, rownum
from
(
select a from test2 group by a
)
)
);
select * from table(dbms_xplan.display(format => 'outline alias'));
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 79 (2)| 00:00:01 |
| 1 | HASH UNIQUE | | 1 | 8 | 79 (2)| 00:00:01 |
|* 2 | HASH JOIN SEMI | | 1 | 8 | 79 (2)| 00:00:01 |
| 3 | VIEW | | 1 | 3 | 3 (0)| 00:00:01 |
| 4 | COUNT | | | | | |
| 5 | VIEW | | 1 | 3 | 3 (0)| 00:00:01 |
| 6 | HASH GROUP BY | | 1 | 3 | 3 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL| TEST2 | 1 | 3 | 3 (0)| 00:00:01 |
| 8 | TABLE ACCESS FULL | TEST1 | 100K| 488K| 76 (2)| 00:00:01 |
---------------------------------------------------------------------------------
Potential Solution #3: Fix cardinality estimates and hope for the best.
If the estimated number of rows is accurate the plan is almost always good. When the row estimates are far off, find the first part of the execution plan where the cardinality is wrong. For this plan, it appears to be plan ID 36. E-Rows and A-Rows are off by an order of magnitude:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | O/1/M |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
...
|* 36 | TABLE ACCESS FULL | POS_CUST_XREF | 1 | 54 | 579 |00:00:00.01 | 131 | 0 | | | |
Step 36 has a complex predicate that involves SYSDATE.
36 - filter(("M"."SG_BILL_TO_CUST_NO" IS NOT NULL AND ("M"."INDIRECT_DISTI"='Y' OR "M"."CATEGORY_TYPE" LIKE 'DIRECT%') AND "M"."ACTIVE_IND"<>'N' AND
TRUNC(INTERNAL_FUNCTION("M"."ARCHIVE_DATE"))>TRUNC(SYSDATE#!)))
Even with up-to-date statistics that condition is going to be difficult to predict. Dynamic sampling may help. Try re-running the query with a top-level hint like this:
SELECT /*+ dynamic_sampling(6) */ ...
Fixing those early discrepancies will usually fix other problems later in the plan. This example is only one possible source of cardinality mismatches. Other tricks may be necessary to improve other cardinality estimates. This can be a very difficult method but it can pay off in multiple ways.
Red Herrings
There are many potential improvements to any moderately complicated SQL statement. There are several good ideas in the comments and answers. But when tuning it is always imperative to focus on what is slowest, not what is easiest to fix. It sounds obvious, but it's a very easy trap to fall into. That's why I asked you to use /*+ gather_plan_statistics*/, and that's why my answer only focuses on the parts of the plan with a large actual time.
For example, in my earlier comment I suggested looking at the NESTED LOOPS where ROWS=1. Now that we have the actual time we know that suggestion is not helpful. (Although in general you should still be skeptical of a plan with large tables but ROWS=1.)
Before rewriting the query, could you please reply with plan and run time for this query?
Follow Up:
Adding Parallel hint
Adding partition clause in select
The precompute_subquery hint will take subquery text out of the subquery section, fire it separately (before running main query) in a recursive call context, fetch the results and pass these to main query "filter" condition as a list of OR conditions. This is called subquery unfolding I think and it's different from the query block unparsing which is used for distributed queries. I've seen it used in OLAP queries.
As this is an undocumented hint, it should not be used by developers! The subquery is actually executed during soft parsing time, thus multiple executions of the same child cursor may potentially return wrong results if resultset of the subquery changes (unless Oracle always forces another full parse of these cursors somehow - in which case you can end up with library cache/shared pool latch contention if misusing this feature). So I removed it and used parallel for huge tables.
Also dont use Order by which overkills the query run time.
SELECT
DISTINCT GROUP_DIST_NUMBER,
BEGIN_DATE,
PRICE_DROP_DATE
FROM
(SELECT /*+ PARALLEL (P,4) PARALLEL (L,4)*/
DISTINCT
G.GROUP_DIST_NUMBER,
TO_DATE ( :B2,
'DD-MON-YYYY' )
BEGIN_DATE,
TO_DATE ( :B2,
'DD-MON-YYYY' )
PRICE_DROP_DATE
FROM
POS_DISTI_GROUP G,
POS_CUST_XREF M,
S_CPT_SEQ_NO C,
PP_STD_PRICE P,
S_CPT_AUDIT A,
RPT_PRODUCT_VALUE_LEVEL L
WHERE
G.END_DATE > TO_DATE ( :B2,
'DD-MON-YYYY' )
AND G.GROUP_DIST_NUMBER = M.DIST_NUMBER
AND M.SG_BILL_TO_CUST_NO = A.BILL_TO_CUST_NO
AND A.START_DATE <= TO_DATE ( :B2,
'DD-MON-YYYY' )
AND A.END_DATE >= TO_DATE ( :B2,
'DD-MON-YYYY' )
AND L.PROD_VALUE = P.PROD_VALUE
AND L.PROD_LEVEL = P.PROD_LEVEL
AND C.CPT_PRICE_CODE IN
(SELECT /*+ PARALLEL (P1,4)*/
DISTINCT C1.CPT_PRICE_CODE
FROM
PP_STD_PRICE P1,
S_CPT_PRICE_CODE C1,
S_CPT_SEQ_NO S1
WHERE
P1.STDP_ID = :B1
AND C1.CPT_PRICE_CAT LIKE 'NB%'
AND C1.CPT_PRICE_CODE = S1.CPT_PRICE_CODE
AND S1.PRICE_PROTECTABLE = 'Y')
AND C.CPT_PRICE_CODE = P.CUST_PRICE_TYPE
AND P.STDP_ID = :B1
AND A.CUST_PRICE_TYPE = C.CPT_BILL_CODE
AND M.ACTIVE_IND != 'N'
AND ( M.CATEGORY_TYPE LIKE 'DIRECT%'
OR M.INDIRECT_DISTI = 'Y' )
AND TRUNC ( M.ARCHIVE_DATE ) > TRUNC ( SYSDATE )
UNION
SELECT /*+ PARALLEL (P,4) PARALLEL (L,4) */
G.GROUP_DIST_NUMBER,
P.BEGIN_DATE,
MIN ( INVT.PRICE_DROP_DATE ) PRICE_DROP_DATE
FROM
POS_DISTI_GROUP G,
POS_CUST_XREF M,
PP_DEBIT_AUTHORIZATION P,
RPT_PRODUCT_VALUE_LEVEL L,
POS_PP_INVENTORY PARTITION ("F2011_Q2") INVT
WHERE
G.END_DATE > TO_DATE ( :B2,
'DD-MON-YYYY' )
AND G.GROUP_DIST_NUMBER = M.DIST_NUMBER
AND M.ACTIVE_IND != 'N'
AND ( M.CATEGORY_TYPE LIKE 'DIRECT%'
OR M.INDIRECT_DISTI = 'Y' )
AND G.DIST_NUMBER = P.DIST_NUMBER
AND L.PROD_VALUE = P.PROD_VALUE
AND L.PROD_LEVEL = P.PROD_LEVEL
AND P.BEGIN_DATE >= TO_DATE ( :B2,
'DD-MON-YYYY' )
- 6
AND P.BEGIN_DATE <= TO_DATE ( :B2,
'DD-MON-YYYY' )
AND INVT.DIST_NUMBER = G.GROUP_DIST_NUMBER
AND INVT.STMODEL = L.MOD_DESC
AND INVT.PPCF_SHOW_DATE = P.BEGIN_DATE
AND P.PRICE_TYPE = 'I'
AND ( P.POS_PROCESSED_FLAG IS NULL
OR P.POS_PROCESSED_FLAG != 'C' )
AND P.POS_PP_FLAG = 'Y'
AND TRUNC ( M.ARCHIVE_DATE ) > TRUNC ( SYSDATE )
GROUP BY
G.GROUP_DIST_NUMBER,
P.BEGIN_DATE);
EXPLAIN PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 101 | 2525 | 8500 (17)| | | | | |
| 1 | PX COORDINATOR | | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10005 | 101 | 2525 | 8500 (17)| | | Q1,05 | P->S | QC (RAND) |
| 3 | VIEW | | 101 | 2525 | 8500 (17)| | | Q1,05 | PCWP | |
| 4 | SORT UNIQUE | | 101 | 18291 | 8500 (82)| | | Q1,05 | PCWP | |
| 5 | PX RECEIVE | | | | | | | Q1,05 | PCWP | |
| 6 | PX SEND HASH | :TQ10004 | | | | | | Q1,04 | P->P | HASH |
| 7 | BUFFER SORT | | 101 | 2525 | | | | Q1,04 | PCWP | |
| 8 | UNION-ALL | | | | | | | Q1,04 | PCWP | |
| 9 | BUFFER SORT | | | | | | | Q1,04 | PCWC | |
| 10 | PX RECEIVE | | | | | | | Q1,04 | PCWP | |
| 11 | PX SEND ROUND-ROBIN | :TQ10001 | | | | | | | S->P | RND-ROBIN |
| 12 | MERGE JOIN CARTESIAN | | 10M| 1737M| 1635 (5)| | | | | |
|* 13 | HASH JOIN | | 2439 | 419K| 322 (3)| | | | | |
|* 14 | TABLE ACCESS FULL | POS_DISTI_GROUP | 100 | 1800 | 5 (0)| | | | | |
|* 15 | HASH JOIN | | 2452 | 378K| 317 (3)| | | | | |
|* 16 | TABLE ACCESS FULL | S_CPT_SEQ_NO | 651 | 5208 | 5 (0)| | | | | |
| 17 | NESTED LOOPS | | 580 | 87000 | 311 (2)| | | | | |
| 18 | NESTED LOOPS | | 34 | 4658 | 131 (4)| | | | | |
|* 19 | HASH JOIN | | 1 | 97 | 109 (5)| | | | | |
|* 20 | HASH JOIN | | 9 | 774 | 107 (5)| | | | | |
|* 21 | HASH JOIN | | 2 | 146 | 101 (4)| | | | | |
|* 22 | TABLE ACCESS FULL | POS_CUST_XREF | 54 | 2268 | 25 (4)| | | | | |
|* 23 | TABLE ACCESS FULL | S_CPT_AUDIT | 68 | 2108 | 76 (4)| | | | | |
| 24 | TABLE ACCESS FULL | S_CPT_SEQ_NO | 1301 | 16913 | 5 (0)| | | | | |
|* 25 | TABLE ACCESS FULL | S_CPT_PRICE_CODE | 21 | 231 | 2 (0)| | | | | |
|* 26 | INDEX RANGE SCAN | UK_PP_STD_PRICE_STDP_ID | 26 | 1040 | 22 (0)| | | | | |
|* 27 | VIEW | RPT_PRODUCT_VALUE_LEVEL | 1 | 13 | 5 (0)| | | | | |
| 28 | UNION ALL PUSHED PREDICATE | | | | | | | | | |
| 29 | NESTED LOOPS OUTER | | 1 | 46 | 2 (0)| | | | | |
| 30 | NESTED LOOPS OUTER | | 1 | 42 | 2 (0)| | | | | |
| 31 | TABLE ACCESS BY INDEX ROWID | SMA_STMODEL | 1 | 28 | 2 (0)| | | | | |
|* 32 | INDEX UNIQUE SCAN | PK_SMA_STMODEL | 1 | | 1 (0)| | | | | |
|* 33 | INDEX UNIQUE SCAN | PK_SEAEGO_PRODUCT_HIERARCHY | 298 | 4172 | 0 (0)| | | | | |
|* 34 | INDEX UNIQUE SCAN | PK_S_CAP_GROUP | 2 | 8 | 0 (0)| | | | | |
| 35 | NESTED LOOPS OUTER | | 1 | 77 | 2 (0)| | | | | |
| 36 | NESTED LOOPS OUTER | | 1 | 73 | 2 (0)| | | | | |
| 37 | NESTED LOOPS | | 1 | 59 | 2 (0)| | | | | |
| 38 | NESTED LOOPS OUTER | | 1 | 31 | 2 (0)| | | | | |
| 39 | NESTED LOOPS OUTER | | 1 | 27 | 2 (0)| | | | | |
| 40 | TABLE ACCESS BY INDEX ROWID| SMA_PRODUCTMODEL | 1 | 23 | 2 (0)| | | | | |
|* 41 | INDEX UNIQUE SCAN | PK_SMA_PRODUCTMODEL | 1 | | 1 (0)| | | | | |
|* 42 | INDEX UNIQUE SCAN | PK_S_FAMILY | 1366 | 5464 | 0 (0)| | | | | |
|* 43 | INDEX UNIQUE SCAN | PK_F_MODPRODMGR | 22 | 88 | 0 (0)| | | | | |
| 44 | TABLE ACCESS BY INDEX ROWID | SMA_STMODEL | 13965 | 381K| 1 (0)| | | | | |
|* 45 | INDEX UNIQUE SCAN | PK_SMA_STMODEL | 1 | | 0 (0)| | | | | |
|* 46 | INDEX UNIQUE SCAN | PK_SEAEGO_PRODUCT_HIERARCHY | 298 | 4172 | 0 (0)| | | | | |
|* 47 | INDEX UNIQUE SCAN | PK_S_CAP_GROUP | 2 | 8 | 0 (0)| | | | | |
|* 48 | INDEX UNIQUE SCAN | IDX_RPT_PROD_MV_PROD_NO | 1 | 12 | 1 (0)| | | | | |
| 49 | BUFFER SORT | | 4128 | 20640 | 1629 (5)| | | | | |
|* 50 | INDEX RANGE SCAN | UK_PP_STD_PRICE_STDP_ID | 4128 | 20640 | 23 (0)| | | | | |
| 51 | HASH GROUP BY | | 1 | 191 | 5578 (2)| | | Q1,04 | PCWP | |
| 52 | PX RECEIVE | | 1 | 191 | 5578 (2)| | | Q1,04 | PCWP | |
| 53 | PX SEND HASH | :TQ10003 | 1 | 191 | 5578 (2)| | | Q1,03 | P->P | HASH |
| 54 | HASH GROUP BY | | 1 | 191 | 5578 (2)| | | Q1,03 | PCWP | |
|* 55 | FILTER | | | | | | | Q1,03 | PCWC | |
| 56 | NESTED LOOPS | | 1 | 191 | 5575 (2)| | | Q1,03 | PCWP | |
| 57 | NESTED LOOPS | | 7 | 1176 | 5562 (2)| | | Q1,03 | PCWP | |
|* 58 | HASH JOIN | | 74 | 8584 | 1347 (3)| | | Q1,03 | PCWP | |
| 59 | BUFFER SORT | | | | | | | Q1,03 | PCWC | |
| 60 | PX RECEIVE | | 60 | 3780 | 31 (7)| | | Q1,03 | PCWP | |
| 61 | PX SEND HASH | :TQ10000 | 60 | 3780 | 31 (7)| | | | S->P | HASH |
|* 62 | HASH JOIN | | 60 | 3780 | 31 (7)| | | | | |
|* 63 | TABLE ACCESS FULL | POS_CUST_XREF | 60 | 2100 | 25 (4)| | | | | |
|* 64 | TABLE ACCESS FULL | POS_DISTI_GROUP | 100 | 2800 | 5 (0)| | | | | |
| 65 | PX RECEIVE | | 345 | 18285 | 1316 (2)| | | Q1,03 | PCWP | |
| 66 | PX SEND HASH | :TQ10002 | 345 | 18285 | 1316 (2)| | | Q1,02 | P->P | HASH |
| 67 | PX BLOCK ITERATOR | | 345 | 18285 | 1316 (2)| | | Q1,02 | PCWC | |
|* 68 | TABLE ACCESS FULL | PP_DEBIT_AUTHORIZATION | 345 | 18285 | 1316 (2)| | | Q1,02 | PCWP | |
| 69 | PARTITION RANGE ALL | | 1 | 52 | 205 (1)| 1 | 33 | Q1,03 | PCWP | |
|* 70 | INDEX RANGE SCAN | POS_PP_INVENTORY_PK | 1 | 52 | 205 (1)| 1 | 33 | Q1,03 | PCWP | |
|* 71 | VIEW | RPT_PRODUCT_VALUE_LEVEL | 1 | 23 | 7 (0)| | | Q1,03 | PCWP | |
| 72 | UNION ALL PUSHED PREDICATE | | | | | | | Q1,03 | PCWP | |
|* 73 | FILTER | | | | | | | Q1,03 | PCWP | |
| 74 | NESTED LOOPS OUTER | | 1 | 46 | 2 (0)| | | Q1,03 | PCWP | |
| 75 | NESTED LOOPS OUTER | | 1 | 42 | 2 (0)| | | Q1,03 | PCWP | |
| 76 | TABLE ACCESS BY INDEX ROWID | SMA_STMODEL | 1 | 28 | 2 (0)| | | Q1,03 | PCWP | |
|* 77 | INDEX UNIQUE SCAN | PK_SMA_STMODEL | 1 | | 1 (0)| | | Q1,03 | PCWP | |
|* 78 | INDEX UNIQUE SCAN | PK_SEAEGO_PRODUCT_HIERARCHY | 298 | 4172 | 0 (0)| | | Q1,03 | PCWP | |
|* 79 | INDEX UNIQUE SCAN | PK_S_CAP_GROUP | 2 | 8 | 0 (0)| | | Q1,03 | PCWP | |
| 80 | NESTED LOOPS OUTER | | 1 | 77 | 3 (0)| | | Q1,03 | PCWP | |
| 81 | NESTED LOOPS OUTER | | 1 | 73 | 3 (0)| | | Q1,03 | PCWP | |
| 82 | NESTED LOOPS OUTER | | 1 | 69 | 3 (0)| | | Q1,03 | PCWP | |
| 83 | NESTED LOOPS OUTER | | 1 | 65 | 3 (0)| | | Q1,03 | PCWP | |
| 84 | NESTED LOOPS | | 1 | 51 | 3 (0)| | | Q1,03 | PCWP | |
|* 85 | TABLE ACCESS BY INDEX ROWID | SMA_PRODUCTMODEL | 1 | 23 | 2 (0)| | | Q1,03 | PCWP | |
|* 86 | INDEX UNIQUE SCAN | PK_SMA_PRODUCTMODEL | 1 | | 1 (0)| | | Q1,03 | PCWP | |
| 87 | TABLE ACCESS BY INDEX ROWID | SMA_STMODEL | 1 | 28 | 1 (0)| | | Q1,03 | PCWP | |
|* 88 | INDEX UNIQUE SCAN | PK_SMA_STMODEL | 1 | | 0 (0)| | | Q1,03 | PCWP | |
|* 89 | INDEX UNIQUE SCAN | PK_SEAEGO_PRODUCT_HIERARCHY | 298 | 4172 | 0 (0)| | | Q1,03 | PCWP | |
|* 90 | INDEX UNIQUE SCAN | PK_S_FAMILY | 1366 | 5464 | 0 (0)| | | Q1,03 | PCWP | |
|* 91 | INDEX UNIQUE SCAN | PK_S_CAP_GROUP | 2 | 8 | 0 (0)| | | Q1,03 | PCWP | |
|* 92 | INDEX UNIQUE SCAN | PK_F_MODPRODMGR | 22 | 88 | 0 (0)| | | Q1,03 | PCWP | |
|* 93 | MAT_VIEW ACCESS BY INDEX ROWID | RPT_PROD_MV | 1 | 24 | 2 (0)| | | Q1,03 | PCWP | |
|* 94 | INDEX UNIQUE SCAN | IDX_RPT_PROD_MV_PROD_NO | 1 | | 1 (0)| | | Q1,03 | PCWP | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
13 - access("G"."GROUP_DIST_NUMBER"="M"."DIST_NUMBER")
14 - filter("G"."END_DATE">TO_DATE(:B2,'DD-MON-YYYY'))
15 - access("C1"."CPT_PRICE_CODE"="S1"."CPT_PRICE_CODE")
16 - filter("S1"."PRICE_PROTECTABLE"='Y')
19 - access("C"."CPT_PRICE_CODE"="C1"."CPT_PRICE_CODE")
20 - access("A"."CUST_PRICE_TYPE"="C"."CPT_BILL_CODE")
21 - access("M"."SG_BILL_TO_CUST_NO"="A"."BILL_TO_CUST_NO")
22 - filter("M"."SG_BILL_TO_CUST_NO" IS NOT NULL AND ("M"."INDIRECT_DISTI"='Y' OR "M"."CATEGORY_TYPE" LIKE 'DIRECT%') AND "M"."ACTIVE_IND"<>'N' AND
TRUNC(INTERNAL_FUNCTION("M"."ARCHIVE_DATE"))>TRUNC(SYSDATE#!))
23 - filter("A"."START_DATE"<=TO_DATE(:B2,'DD-MON-YYYY') AND "A"."END_DATE">=TO_DATE(:B2,'DD-MON-YYYY'))
25 - filter("C1"."CPT_PRICE_CAT" LIKE 'NB%')
26 - access("P"."STDP_ID"=TO_NUMBER(:B1) AND "C"."CPT_PRICE_CODE"="P"."CUST_PRICE_TYPE")
filter("C"."CPT_PRICE_CODE"="P"."CUST_PRICE_TYPE")
27 - filter("L"."PROD_LEVEL"="P"."PROD_LEVEL")
32 - access("ST"."MOD_DESC"="P"."PROD_VALUE")
33 - access("ST"."MARKETING_NAME"="PH"."MARKETING_NAME"(+))
34 - access("ST"."MOD_CAPACITY_FORMATTED"="SCG"."MOD_CAPACITY_FORMATTED"(+))
41 - access("PM"."MODEL"="P"."PROD_VALUE")
42 - access("SF"."FAMILY"(+)=SUBSTRB("PM"."MODEL",1,3))
43 - access("PM"."DESIGN_APPLICATION"="DA"."DESIGN_APPLICATION"(+))
45 - access("PM"."MOD_DESC"="ST"."MOD_DESC")
46 - access("ST"."MARKETING_NAME"="PH"."MARKETING_NAME"(+))
47 - access("ST"."MOD_CAPACITY_FORMATTED"="SCG"."MOD_CAPACITY_FORMATTED"(+))
48 - access("PROD_NO"="P"."PROD_VALUE")
50 - access("P1"."STDP_ID"=TO_NUMBER(:B1))
55 - filter(TO_DATE(:B2,'DD-MON-YYYY')-6<=TO_DATE(:B2,'DD-MON-YYYY'))
58 - access("G"."DIST_NUMBER"="P"."DIST_NUMBER")
62 - access("G"."GROUP_DIST_NUMBER"="M"."DIST_NUMBER")
63 - filter(("M"."INDIRECT_DISTI"='Y' OR "M"."CATEGORY_TYPE" LIKE 'DIRECT%') AND "M"."ACTIVE_IND"<>'N' AND
TRUNC(INTERNAL_FUNCTION("M"."ARCHIVE_DATE"))>TRUNC(SYSDATE#!))
64 - filter("G"."END_DATE">TO_DATE(:B2,'DD-MON-YYYY'))
68 - filter("P"."PRICE_TYPE"='I' AND "P"."POS_PP_FLAG"='Y' AND ("P"."POS_PROCESSED_FLAG"<>'C' OR "P"."POS_PROCESSED_FLAG" IS NULL) AND
"P"."BEGIN_DATE"<=TO_DATE(:B2,'DD-MON-YYYY') AND "P"."BEGIN_DATE">=TO_DATE(:B2,'DD-MON-YYYY')-6)
70 - access("INVT"."DIST_NUMBER"="G"."GROUP_DIST_NUMBER" AND "INVT"."PPCF_SHOW_DATE"="P"."BEGIN_DATE")
filter("INVT"."PPCF_SHOW_DATE"<=TO_DATE(:B2,'DD-MON-YYYY') AND "INVT"."PPCF_SHOW_DATE">=TO_DATE(:B2,'DD-MON-YYYY')-6 AND
"INVT"."PPCF_SHOW_DATE"="P"."BEGIN_DATE")
71 - filter("L"."PROD_LEVEL"="P"."PROD_LEVEL")
73 - filter("P"."PROD_VALUE"="INVT"."STMODEL")
77 - access("ST"."MOD_DESC"="P"."PROD_VALUE")
78 - access("ST"."MARKETING_NAME"="PH"."MARKETING_NAME"(+))
79 - access("ST"."MOD_CAPACITY_FORMATTED"="SCG"."MOD_CAPACITY_FORMATTED"(+))
85 - filter("PM"."MOD_DESC"="INVT"."STMODEL")
86 - access("PM"."MODEL"="P"."PROD_VALUE")
88 - access("ST"."MOD_DESC"="INVT"."STMODEL")
89 - access("ST"."MARKETING_NAME"="PH"."MARKETING_NAME"(+))
90 - access("SF"."FAMILY"(+)=SUBSTRB("PM"."MODEL",1,3))
91 - access("ST"."MOD_CAPACITY_FORMATTED"="SCG"."MOD_CAPACITY_FORMATTED"(+))
92 - access("PM"."DESIGN_APPLICATION"="DA"."DESIGN_APPLICATION"(+))
93 - filter("MOD_DESC"="INVT"."STMODEL")
94 - access("PROD_NO"="P"."PROD_VALUE")
Note
-----
- 'PLAN_TABLE' is old version
PS:
Did you manage to get Run time? Also I change the query, please test this and let us know the result. Ideally the first query should be faster than yours and the second one (replaced the first) should be better than the first one.Remember to keep them separately with notes on performance.
just guess, but look like your statement not using index on archive_date column
try to use
AND M.ARCHIVE_DATE > TRUNC (SYSDATE) + 1 - 1/24/60/60
instead of
AND TRUNC (M.ARCHIVE_DATE) > TRUNC (SYSDATE)
also you should post all tables description into question
SELECT /*+ PARALLEL (P,4) PARALLEL (L,4)*/
DISTINCT
G.GROUP_DIST_NUMBER,
TO_DATE ( :B2,
'DD-MON-YYYY' )
BEGIN_DATE,
TO_DATE ( :B2,
'DD-MON-YYYY' )
PRICE_DROP_DATE
FROM
POS_DISTI_GROUP G,
POS_CUST_XREF M,
S_CPT_SEQ_NO C,
PP_STD_PRICE P,
S_CPT_AUDIT A,
RPT_PRODUCT_VALUE_LEVEL L
WHERE 1=0 -- switched off
G.END_DATE > TO_DATE ( :B2,
'DD-MON-YYYY' )
AND G.GROUP_DIST_NUMBER = M.DIST_NUMBER
AND M.SG_BILL_TO_CUST_NO = A.BILL_TO_CUST_NO
AND A.START_DATE <= TO_DATE ( :B2,
'DD-MON-YYYY' )
AND A.END_DATE >= TO_DATE ( :B2,
'DD-MON-YYYY' )
AND L.PROD_VALUE = P.PROD_VALUE
AND L.PROD_LEVEL = P.PROD_LEVEL
AND C.CPT_PRICE_CODE IN
(SELECT /*+ PARALLEL (P1,4)*/
DISTINCT C1.CPT_PRICE_CODE
FROM
PP_STD_PRICE P1,
S_CPT_PRICE_CODE C1,
S_CPT_SEQ_NO S1
WHERE
P1.STDP_ID = :B1
AND C1.CPT_PRICE_CAT LIKE 'NB%'
AND C1.CPT_PRICE_CODE = S1.CPT_PRICE_CODE
AND S1.PRICE_PROTECTABLE = 'Y')
AND C.CPT_PRICE_CODE = P.CUST_PRICE_TYPE
AND P.STDP_ID = :B1
AND A.CUST_PRICE_TYPE = C.CPT_BILL_CODE
AND M.ACTIVE_IND != 'N'
AND ( M.CATEGORY_TYPE LIKE 'DIRECT%'
OR M.INDIRECT_DISTI = 'Y' )
AND TRUNC ( M.ARCHIVE_DATE ) > TRUNC ( SYSDATE )
UNION
SELECT /*+ PARALLEL (P,4) PARALLEL (L,4) */
G.GROUP_DIST_NUMBER,
P.BEGIN_DATE,
MIN ( INVT.PRICE_DROP_DATE ) PRICE_DROP_DATE
FROM
POS_DISTI_GROUP G,
POS_CUST_XREF M,
PP_DEBIT_AUTHORIZATION P,
RPT_PRODUCT_VALUE_LEVEL L,
POS_PP_INVENTORY PARTITION ("F2011_Q2") INVT
WHERE 1=0 -- switched off
G.END_DATE > TO_DATE ( :B2,
'DD-MON-YYYY' )
AND G.GROUP_DIST_NUMBER = M.DIST_NUMBER
AND M.ACTIVE_IND != 'N'
AND ( M.CATEGORY_TYPE LIKE 'DIRECT%'
OR M.INDIRECT_DISTI = 'Y' )
AND G.DIST_NUMBER = P.DIST_NUMBER
AND L.PROD_VALUE = P.PROD_VALUE
AND L.PROD_LEVEL = P.PROD_LEVEL
AND P.BEGIN_DATE >= TO_DATE ( :B2,
'DD-MON-YYYY' )
- 6
AND P.BEGIN_DATE <= TO_DATE ( :B2,
'DD-MON-YYYY' )
AND INVT.DIST_NUMBER = G.GROUP_DIST_NUMBER
AND INVT.STMODEL = L.MOD_DESC
AND INVT.PPCF_SHOW_DATE = P.BEGIN_DATE
AND P.PRICE_TYPE = 'I'
AND ( P.POS_PROCESSED_FLAG IS NULL
OR P.POS_PROCESSED_FLAG != 'C' )
AND P.POS_PP_FLAG = 'Y'
AND TRUNC ( M.ARCHIVE_DATE ) > TRUNC ( SYSDATE )
GROUP BY
G.GROUP_DIST_NUMBER,
P.BEGIN_DATE
Result From OP:
After try to switch off and on here there result
1. Switch off 1st where condition
2.Switch off 2nd where condition
3.Switch off 3rd where condition , it started to long running, so i guess the select query at 2nd condition taken long time on execution
The scenerio
explain plan for
select l.etl_id , v.*
from v_load_base v, etl_log l
where l.is_active = 1
and v.ddate between trunc(l.load_from_date) and l.load_to_date
and v.starttime_full between l.load_from_date and l.load_to_date;
Produces this execution plan
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 444 | | 31624 (4)| 00:06:20 |
| 1 | SORT ORDER BY | | 3 | 444 | | 31624 (4)| 00:06:20 |
|* 2 | HASH JOIN | | 3 | 444 | | 31623 (4)| 00:06:20 |
| 3 | NESTED LOOPS OUTER | | 3 | 378 | | 31413 (4)| 00:06:17 |
|* 4 | HASH JOIN | | 3 | 348 | | 31410 (4)| 00:06:17 |
|* 5 | HASH JOIN | | 1252 | 118K| 2144K| 23428 (4)| 00:04:42 |
|* 6 | HASH JOIN | | 27786 | 1818K| | 764 (7)| 00:00:10 |
| 7 | NESTED LOOPS | | 8 | 264 | | 7 (0)| 00:00:01 |
|* 8 | TABLE ACCESS FULL | ETL_LOG | 1 | 21 | | 3 (0)| 00:00:01 |
|* 9 | TABLE ACCESS FULL | MD | 8 | 96 | | 4 (0)| 00:00:01 |
| 10 | TABLE ACCESS FULL | DS | 479K| 15M| | 748 (6)| 00:00:09 |
| 11 | TABLE ACCESS FULL | MDS | 7280K| 208M| | 7823 (5)| 00:01:34 |
| 12 | TABLE ACCESS FULL | TASKS | 7760K| 140M| | 7844 (5)| 00:01:35 |
| 13 | TABLE ACCESS BY INDEX ROWID| ETL_GIS | 1 | 10 | | 1 (0)| 00:00:01 |
|* 14 | INDEX UNIQUE SCAN | ETL_GIS_UK | 1 | | | 0 (0)| 00:00:01 |
| 15 | TABLE ACCESS FULL | DETAILS_TABLE | 292K| 6280K| | 204 (8)| 00:00:03 |
--------------------------------------------------------------------------------------------------------
The join predicate with the table etl_log was pushed down to the view v_load_base (line 8).
I created a view called v_load_base_active based on the same exact query as the one above.
Querying the new view produces the following plan
explain plan for select * from v_load_base_active;
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 861 | | 63583 (8)| 00:12:43 |
| 1 | NESTED LOOPS | | 3 | 861 | | 63583 (8)| 00:12:43 |
|* 2 | TABLE ACCESS FULL | ETL_LOG | 1 | 21 | | 3 (0)| 00:00:01 |
|* 3 | VIEW | V_LOAD_BASE | 3 | 798 | | 63580 (8)| 00:12:43 |
| 4 | SORT ORDER BY | | 422K| 51M| 110M| 63580 (8)| 00:12:43 |
|* 5 | HASH JOIN RIGHT OUTER | | 422K| 51M| | 51513 (9)| 00:10:19 |
| 6 | TABLE ACCESS FULL | ETL_GIS | 5958 | 59580 | | 17 (0)| 00:00:01 |
|* 7 | HASH JOIN | | 422K| 47M| 9712K| 51488 (9)| 00:10:18 |
| 8 | TABLE ACCESS FULL | LINES_DETAILS | 292K| 6280K| | 204 (8)| 00:00:03 |
|* 9 | HASH JOIN | | 422K| 38M| 35M| 48647 (10)| 00:09:44 |
|* 10 | HASH JOIN | | 422K| 30M| | 27365 (14)| 00:05:29 |
| 11 | TABLE ACCESS FULL | MD | 3103 | 37236 | | 4 (0)| 00:00:01 |
|* 12 | HASH JOIN | | 7301K| 445M| 21M| 24366 (3)| 00:04:53 |
| 13 | TABLE ACCESS FULL| DS | 479K| 15M| | 748 (6)| 00:00:09 |
| 14 | TABLE ACCESS FULL| MSD | 7280K| 208M| | 7823 (5)| 00:01:34 |
| 15 | TABLE ACCESS FULL | TASKS | 7760K| 140M| | 7844 (5)| 00:01:35 |
----------------------------------------------------------------------------------------------------
The predicate is not pushed. This leads to a major decrease in performance.
I've tried setting a hint explicitly in the view /*+ PUSH_PRED(v) */ but the plan does not change.
How can i make the optimizer push the predicate also within a view ... ?
v_load_base does not contain analytic functions. The first query proves that the predicate can be pushed.
EDIT
notice that oracle does not state in the execution plan that a predicate was pushed with VIEW PUSHED PREDICATE. but, looking at the plan it's clear that oracle transformed the view's sql to include the etl_log predicate.
I doubt that it was pushing predicate in the first case, because it would be in the plan. More likely it was merging which is controlled by MERGE/NO_MERGE hints. See example below.
With NO_MERGE:
SQL> explain plan for
2 select /*+NO_MERGE(so)*/ *
3 from siebel.s_org_ext soe,
4 (select sx.attrib_08, s.*
5 from siebel.s_opty s
6 inner join siebel.s_opty_x sx on s.row_id = sx.row_id) so
7 where soe.row_id = so.pr_dept_ou_id
8 and soe.row_id like '1-8ZT%'
9 and so.db_last_upd between soe.db_last_upd and soe.db_last_upd - 365;
Explained
SQL> select * from table(dbms_xplan.display);
Plan hash value: 1802470607
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13258 | 55 (2)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 13258 | 55 (2)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| S_ORG_EXT | 1 | 1047 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | S_ORG_EXT_P1 | 1 | | 2 (0)| 00:00:01 |
| 4 | VIEW | | 1084 | 12M| 52 (2)| 00:00:01 |
|* 5 | HASH JOIN | | 1084 | 528K| 52 (2)| 00:00:01 |
| 6 | TABLE ACCESS FULL | S_OPTY_X | 1573 | 15730 | 17 (0)| 00:00:01 |
|* 7 | TABLE ACCESS FULL | S_OPTY | 1084 | 517K| 34 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
With MERGE:
SQL> explain plan for
2 select /*+MERGE(so)*/*
3 from siebel.s_org_ext soe,
4 (select sx.attrib_08, s.*
5 from siebel.s_opty s
6 inner join siebel.s_opty_x sx on s.row_id = sx.row_id) so
7 where soe.row_id = so.pr_dept_ou_id
8 and soe.row_id like '1-8ZT%'
9 and so.db_last_upd between soe.db_last_upd and soe.db_last_upd - 365;
Explained
SQL> select * from table(dbms_xplan.display);
Plan hash value: 4111959163
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1546 | 6 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 1546 | 6 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 1536 | 5 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| S_ORG_EXT | 1 | 1047 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | S_ORG_EXT_P1 | 1 | | 2 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| S_OPTY | 1 | 489 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | S_OPTY_M64_X | 1 | | 1 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID | S_OPTY_X | 1 | 10 | 1 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | S_OPTY_X_P1 | 1 | | 0 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
So try to force optimizer use merging with you view and see if plan changes.
I'm having a big performance problem with the following query. And need help to make it as fast as possible.
VIEW_SHIPMENT_ORDER_RELEASE got 2 million rows and I'm sure that I can make a better query to speed this. The application is taking almost 2 minutes to run.
SELECT O.ORDER_RELEASE_GID
FROM ORDER_RELEASE O, ORDER_RELEASE_STATUS S
WHERE O.ORDER_RELEASE_GID = S.ORDER_RELEASE_GID
AND S.STATUS_TYPE_GID = 'STATUS'
AND S.STATUS_VALUE_GID IN ('OPEN', 'OPEN-HANDLE')
AND O.SOURCE_LOCATION_GID = '114'
AND O.ORDER_RELEASE_GID NOT IN
(SELECT V.ORDER_RELEASE_GID FROM VIEW_SHIPMENT_ORDER_RELEASE V
WHERE V.ORDER_RELEASE_GID = O.ORDER_RELEASE_GID)
Here's the view code:
create or replace view glogowner.view_shipment_order_release as
select distinct shp.perspective, shp.shipment_gid, ssul.order_release_gid
from shipment shp,
shipment_s_equipment_join ssej,
s_equipment_s_ship_unit_join sessuj,
s_ship_unit_line ssul
where shp.shipment_gid = ssej.shipment_gid
and ssej.s_equipment_gid = sessuj.s_equipment_gid
and sessuj.s_ship_unit_gid = ssul.s_ship_unit_gid
and ssul.order_release_gid is not null
The explain plan:
1 Plan hash value: 1257125198
2
3 --------------------------------------------------------------------------------------------------------------------------------------
4 | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Inst |
5 --------------------------------------------------------------------------------------------------------------------------------------
6 | 0 | SELECT STATEMENT REMOTE | | 314 | 98596 | | 35795 (1)| 00:07:10 | |
7 | 1 | NESTED LOOPS | | | | | | | |
8 | 2 | NESTED LOOPS | | 314 | 98596 | | 35795 (1)| 00:07:10 | |
9 |* 3 | HASH JOIN ANTI | | 201 | 48441 | | 35192 (1)| 00:07:03 | |
10 | 4 | TABLE ACCESS BY INDEX ROWID | ORDER_RELEASE | 20104 | 726K| | 3893 (1)| 00:00:47 | ABC123 |
11 |* 5 | INDEX RANGE SCAN | OR_SOURCE_LOCATION_GID | 20104 | | | 157 (0)| 00:00:02 | ABC123 |
12 | 6 | VIEW | VW_SQ_1 | 1515K| 294M| | 31293 (1)| 00:06:16 | ABC123 |
13 |* 7 | HASH JOIN | | 1515K| 144M| | 31293 (1)| 00:06:16 | |
14 | 8 | INDEX STORAGE FAST FULL SCAN | IND_SSEJ_SEQUIPGID | 69218 | 811K| | 91 (0)| 00:00:02 | ABC123 |
15 |* 9 | HASH JOIN | | 1515K| 127M| 73M| 31195 (1)| 00:06:15 | |
16 | 10 | INDEX STORAGE FAST FULL SCAN| PK_S_EQUIPMENT_S_SHIP_UNIT_JOI | 1515K| 56M| | 3958 (1)| 00:00:48 | ABC123 |
17 |* 11 | TABLE ACCESS STORAGE FULL | S_SHIP_UNIT_LINE | 1619K| 75M| | 18893 (1)| 00:03:47 | ABC123 |
18 |* 12 | INDEX UNIQUE SCAN | PK_ORDER_RELEASE_STATUS | 1 | | | 2 (0)| 00:00:01 | ABC123 |
19 |* 13 | TABLE ACCESS BY INDEX ROWID | ORDER_RELEASE_STATUS | 2 | 146 | | 3 (0)| 00:00:01 | ABC123 |
20 --------------------------------------------------------------------------------------------------------------------------------------
21
22 Predicate Information (identified by operation id):
23 ---------------------------------------------------
24
25 3 - access("A2"."ORDER_RELEASE_GID"="ORDER_RELEASE_GID")
26 5 - access("A2"."SOURCE_LOCATION_GID"='114')
27 7 - access("SSEJ"."S_EQUIPMENT_GID"="SESSUJ"."S_EQUIPMENT_GID")
28 9 - access("SESSUJ"."S_SHIP_UNIT_GID"="SSUL"."S_SHIP_UNIT_GID")
29 11 - storage("SSUL"."ORDER_RELEASE_GID" IS NOT NULL)
30 filter("SSUL"."ORDER_RELEASE_GID" IS NOT NULL)
31 12 - access("A2"."ORDER_RELEASE_GID"="A1"."ORDER_RELEASE_GID" AND "A1"."STATUS_TYPE_GID"='STATUS')
32 13 - filter("A1"."STATUS_VALUE_GID"='OPEN' OR "A1"."STATUS_VALUE_GID"='OPEN-HANDLE')
I'd make sure that the following are indexed:
shipment.shipment_gid
shipment_s_equipment_join.s_equipment_gid
s_equipment_s_ship_unit_join.s_ship_unit_gid
s_ship_unit_line.order_release_gid
The NOT IN might work better as a NOT EXISTS.
After doing a select, I am doing a self-join and a distinct on the data. I am not sure if there is a better way to write this query. Could you suggest?
Below is the Query, and the explain plan output.
WITH data
AS ( SELECT san.S1FK_C_ID,
san.S1FK_CF_ID,
san.S1FK_A_ID,
san.S1FK_A_TYPE,
san.S1FK_S_TYPE,
san.S1FK_B_FUNC,
AU.AS1FK_A_ID,
AU.AS1FK_B_FUNC,
AU.AS1FK_S_TYPE,
AU.AS1FK_T_CODE,
A2FK_B_ID,
A2FK_NBR,
A2FK_C_CD,
A1FK_B_ID,
A1FK_NBR,
A1FK_C_CD,
AU.R_NAME,
IND
FROM N_RULE cnr,
SAN san,
ACCT_USG AU
WHERE AU.R_NAME = CNR.R_NAME
AND AU.A2FK_C_CD = CNR.C_CD
AND AU.AS1FK_B_FUNC = CNR.B_FUNC
AND AU.AS1FK_A_ID = san.AS1FK_ID || ''
AND AU.AS1FK_B_FUNC = san.AS1FK_B_FUNC
AND AU.AS1FK_S_TYPE = san.AS1FK_S_TYPE
AND AU.AS1FK_T_CODE = san.AS1FK_A_TYPE
AND CNR.FM_CODE = 'SP'
AND san.STATUS = 'A'
AND san.AS1FK_B_FUNC = CNR.B_FUNC
ORDER BY AU.AS1FK_A_ID)
SELECT DISTINCT A.A2FK_C_CD AS C_CD,
-- secondary id's
B.S1FK_C_ID AS C_ID_2,
B.S1FK_CF_ID AS CF_ID_2,
B.S1FK_A_ID AS A_ID_2,
B.S1FK_B_FUNC AS B_FUNC_2,
B.S1FK_S_TYPE AS S_TYPE_2,
B.S1FK_A_TYPE AS A_TYPE_2,
--primary id's
A.S1FK_A_ID AS A_ID_1,
A.S1FK_B_FUNC AS B_FUNC_1,
A.S1FK_S_TYPE AS S_TYPE_1,
A.S1FK_A_TYPE AS A_TYPE_1
FROM data A, --A is the set of primary and standalone id's
data B
--B is the primary and secondary id's
WHERE
B .R_NAME = A.R_NAME
--as join
AND B.AS1FK_B_FUNC = A.AS1FK_B_FUNC
AND B.AS1FK_S_TYPE = A.AS1FK_S_TYPE
AND B.AS1FK_T_CODE = A.AS1FK_T_CODE
--accts join
AND B.A2FK_NBR = A.A2FK_NBR
AND B.A2FK_B_ID = A.A2FK_B_ID
AND B.A2FK_C_CD = A.A2FK_C_CD
AND B.A1FK_NBR = A.A1FK_NBR
AND B.A1FK_B_ID = A.A1FK_B_ID
AND B.A1FK_C_CD = A.A1FK_C_CD
--s fk join
AND B.S1FK_C_ID = A.S1FK_C_ID
AND B.S1FK_CF_ID = A.S1FK_CF_ID
AND B.S1FK_S_TYPE = A.S1FK_S_TYPE
AND B.S1FK_B_FUNC = A.S1FK_B_FUNC
AND B.S1FK_A_TYPE = A.S1FK_A_TYPE
AND ( --join secondary ( N ) and primary id's ( Y ) to primary id's
( 'Y' = A.IND
AND B.IND IN ('N', 'Y') )
OR --join standalone ( ' ' ) id's to themselves
( ' ' = B.IND
AND ' ' = A.IND )
AND B.AS1FK_A_ID = A.AS1FK_A_ID
);
Plan:
PLAN_TABLE_OUTPUT
Plan hash value: 3120521488
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 450 | 18 (17)| 00:00:01 |
| 1 | TEMP TABLE TRANSFORMATION | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D949F_E879D9BC | | | | |
| 3 | SORT ORDER BY | | 1 | 296 | 12 (9)| 00:00:01 |
| 4 | NESTED LOOPS | | 1 | 296 | 11 (0)| 00:00:01 |
| 5 | NESTED LOOPS | | 7412 | 1889K| 11 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL | SAN | 255 | 20910 | 11 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| ACCT_USG | 29 | 5191 | 0 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | ACCT_USG_PK | 1 | | 0 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | N_RULE_PK | 1 | 35 | 0 (0)| 00:00:01 |
| 10 | HASH UNIQUE | | 1 | 450 | 6 (34)| 00:00:01 |
|* 11 | HASH JOIN | | 1 | 450 | 5 (20)| 00:00:01 |
| 12 | VIEW | | 1 | 225 | 2 (0)| 00:00:01 |
| 13 | TABLE ACCESS FULL | SYS_TEMP_0FD9D949F_E879D9BC | 1 | 225 | 2 (0)| 00:00:01 |
| 14 | VIEW | | 1 | 225 | 2 (0)| 00:00:01 |
| 15 | TABLE ACCESS FULL | SYS_TEMP_0FD9D949F_E879D9BC | 1 | 225 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - filter("SAN"."EFF_STATUS_CODE"='A')
8 - access("AU"."AS1FK_A_ID"="SAN"."AS1FK_ID"||'' AND
"AU"."AS1FK_T_CODE"="SAN"."AS1FK_A_TYPE" AND
"AU"."AS1FK_S_TYPE"="SAN"."AS1FK_S_TYPE" AND
"AU"."AS1FK_B_FUNC"="SAN"."AS1FK_B_FUNC")
9 - access("CNR"."FM_CODE"='SP' AND "SAN"."S1FK_C_ID"="CNR"."CLRG_ORG_ID" AND
"SAN"."AS1FK_B_FUNC"="CNR"."B_F_CODE" AND "AU"."A2FK_C_CD"="CNR"."C_CD" AND
"AU"."R_NAME"="CNR"."R_NAME")
filter("AU"."AS1FK_B_FUNC"="CNR"."B_F_CODE")
11 - access("B"."R_NAME"="A"."R_NAME" AND "B"."AS1FK_B_FUNC"="A"."AS1FK_B_FUNC" AND
"B"."AS1FK_S_TYPE"="A"."AS1FK_S_TYPE" AND "B"."AS1FK_T_CODE"="A"."AS1FK_T_CODE"
AND "B"."A2FK_NBR"="A"."A2FK_NBR" AND "B"."A2FK_B_ID"="A"."A2FK_B_ID" AND
"B"."A2FK_C_CD"="A"."A2FK_C_CD" AND "B"."A1FK_NBR"="A"."A1FK_NBR" AND
"B"."A1FK_B_ID"="A"."A1FK_B_ID" AND "B"."A1FK_C_CD"="A"."A1FK_C_CD" AND
"B"."S1FK_C_ID"="A"."S1FK_C_ID" AND "B"."S1FK_CF_ID"="A"."S1FK_CF_ID" AND
"B"."S1FK_S_TYPE"="A"."S1FK_S_TYPE" AND "B"."S1FK_B_FUNC"="A"."S1FK_B_FUNC" AND
"B"."STACO1FK_A_TYPE"="A"."STACO1FK_A_TYPE")
filter("A"."IND"='Y' AND ("B"."IND"='N' OR
"B"."IND"="A"."IND") OR "B"."IND"=' ' AND
"A"."IND"=' ' AND "B"."AS1FK_A_ID"="A"."AS1FK_A_ID" AND
"B"."IND"="A"."IND")
EDIT
~~~~
I edited the above SQL to remove self-joins and unnecessary group-by.
I am doing grouping only on primary_secondary rows based on join.
Between primary and secondary, I am deciding the primary acct that should be plugged in for secondary.
This is giving the same result. I am wondering if this can be further improved.
WITH data
AS ( SELECT sanw.STAC01FK_C_ID,
sanw.STAC01FK_CF_ID,
sanw.STAC01FK_A_ID,
sanw.STACO1FK_A_TYPE,
sanw.STAC01FK_S_TYPE,
sanw.STAC01FK_B_FUNC,
BAU.ASAC01FK_A_ID,
BAU.ASAC01FK_B_FUNC,
BAU.ASAC01FK_S_TYPE,
BAU.ASAC01FK_TYPE_CODE,
BAAC02FK_BANK_ID,
BAAC02FK_NBR,
BAAC02FK_CURR_CD,
BAAC01FK_BANK_ID,
BAAC01FK_NBR,
BAAC01FK_CURR_CD,
BAU.R_NAME,
PRIMARY_ACCT_IND
FROM N_RULE cnr,
SAN sanw,
ACCT_USG bau
WHERE BAU.R_NAME = CNR.R_NAME
AND BAU.BAAC02FK_CURR_CD = CNR.C_CD
AND BAU.ASAC01FK_B_FUNC = CNR.B_FUNC_CD
AND BAU.ASAC01FK_A_ID = sanw.ASAC01FK_ID
AND BAU.ASAC01FK_B_FUNC = sanw.ASAC01FK_B_FUNC
AND BAU.ASAC01FK_S_TYPE = sanw.ASAC01FK_S_TYPE
AND BAU.ASAC01FK_TYPE_CODE = sanw.ASAC01FK_A_TYPE
AND CNR.FM_CODE = 'SP'
AND sanw.EFF_STATUS_CODE = 'A'
AND sanw.ASAC01FK_B_FUNC = CNR.B_FUNC_CD),
primary_secondary
AS (SELECT PRIMARY_ACCT_IND,
B.BAAC02FK_CURR_CD AS C_CD,
B.STAC01FK_C_ID AS C_ID_2,
B.STAC01FK_CF_ID AS CF_ID_2,
-- secondary accounts
B.STAC01FK_A_ID AS A_ID_2,
B.STAC01FK_B_FUNC AS B_FUNC_2,
B.STAC01FK_S_TYPE AS S_TYPE_2,
B.STACO1FK_A_TYPE AS A_TYPE_2,
--primary accounts
B.STAC01FK_A_ID AS A_ID_1,
B.STAC01FK_B_FUNC AS B_FUNC_1,
B.STAC01FK_S_TYPE AS S_TYPE_1,
B.STACO1FK_A_TYPE AS A_TYPE_1,
RANK ()
OVER (
PARTITION BY
--bank acct join
B.BAAC02FK_CURR_CD,
--sa join
B.STAC01FK_S_TYPE,
B.STAC01FK_B_FUNC,
B.STACO1FK_A_TYPE,
B.STAC01FK_C_ID,
B.STAC01FK_CF_ID
ORDER BY
PRIMARY_ACCT_IND DESC)
AS d_rank
FROM data B
WHERE B.PRIMARY_ACCT_IND IN ('Y', 'N')),
stand_alone
AS (SELECT B.BAAC02FK_CURR_CD AS C_CD,
B.STAC01FK_C_ID AS C_ID_2,
B.STAC01FK_CF_ID AS CF_ID_2,
-- secondary accounts
B.STAC01FK_A_ID AS A_ID_2,
B.STAC01FK_B_FUNC AS B_FUNC_2,
B.STAC01FK_S_TYPE AS S_TYPE_2,
B.STACO1FK_A_TYPE AS A_TYPE_2,
--primary accounts
B.STAC01FK_A_ID AS A_ID_1,
B.STAC01FK_B_FUNC AS B_FUNC_1,
B.STAC01FK_S_TYPE AS S_TYPE_1,
B.STACO1FK_A_TYPE AS A_TYPE_1
FROM data B
WHERE B.PRIMARY_ACCT_IND = ' '),
PRIMARY
AS (SELECT C_CD,
C_ID_2,
CF_ID_2,
-- secondary accounts
A_ID_2,
B_FUNC_2,
S_TYPE_2,
A_TYPE_2,
--primary accounts
A_ID_1,
B_FUNC_1,
S_TYPE_1,
A_TYPE_1
FROM primary_secondary
WHERE D_RANK = 1),
SECONDARY
AS (SELECT ps.C_CD,
ps.C_ID_2,
ps.CF_ID_2,
-- secondary accounts
ps.A_ID_2,
ps.B_FUNC_2,
ps.S_TYPE_2,
ps.A_TYPE_2,
--primary accounts
p.A_ID_1,
p.B_FUNC_1,
p.S_TYPE_1,
p.A_TYPE_1
FROM primary_secondary ps, primary p
WHERE ps.D_RANK <> 1
AND ps.C_CD = p.C_CD
AND ps.C_ID_2 = p.C_ID_2
AND ps.CF_ID_2 = p.CF_ID_2
-- secondarary accounts
AND ps.B_FUNC_2 = p.B_FUNC_2
AND ps.S_TYPE_2 = p.S_TYPE_2
AND ps.A_TYPE_2 = p.A_TYPE_2)
SELECT * FROM secondary
UNION ALL
SELECT * FROM primary
UNION ALL
SELECT * FROM stand_alone
PLAN:
Plan hash value: 3159296610
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 33 | 3421 | 9 (56)| 00:00:01 |
| 1 | TEMP TABLE TRANSFORMATION | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9DBAF1_9261CF31 | | | | |
|* 3 | HASH JOIN | | 11 | 3058 | 119 (1)| 00:00:02 |
|* 4 | HASH JOIN | | 96 | 18816 | 104 (1)| 00:00:02 |
|* 5 | INDEX RANGE SCAN | CNR_PK | 61 | 1769 | 1 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL | ACCT_USG | 8244 | 1344K| 102 (0)| 00:00:02 |
|* 7 | TABLE ACCESS FULL | SAN | 1444 | 115K| 15 (0)| 00:00:01 |
| 8 | LOAD AS SELECT | SYS_TEMP_0FD9DBAF2_9261CF31 | | | | |
| 9 | WINDOW SORT | | 11 | 594 | 3 (34)| 00:00:01 |
|* 10 | VIEW | | 11 | 594 | 2 (0)| 00:00:01 |
| 11 | TABLE ACCESS FULL | SYS_TEMP_0FD9DBAF1_9261CF31 | 11 | 2343 | 2 (0)| 00:00:01 |
| 12 | LOAD AS SELECT | SYS_TEMP_0FD9DBAF3_9261CF31 | | | | |
|* 13 | VIEW | | 11 | 1089 | 2 (0)| 00:00:01 |
| 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9DBAF2_9261CF31 | 11 | 682 | 2 (0)| 00:00:01 |
| 15 | UNION-ALL | | | | | |
|* 16 | HASH JOIN | | 11 | 1672 | 5 (20)| 00:00:01 |
|* 17 | VIEW | | 11 | 792 | 2 (0)| 00:00:01 |
| 18 | TABLE ACCESS FULL | SYS_TEMP_0FD9DBAF2_9261CF31 | 11 | 682 | 2 (0)| 00:00:01 |
| 19 | VIEW | | 11 | 880 | 2 (0)| 00:00:01 |
| 20 | TABLE ACCESS FULL | SYS_TEMP_0FD9DBAF3_9261CF31 | 11 | 946 | 2 (0)| 00:00:01 |
| 21 | VIEW | | 11 | 1067 | 2 (0)| 00:00:01 |
| 22 | TABLE ACCESS FULL | SYS_TEMP_0FD9DBAF3_9261CF31 | 11 | 946 | 2 (0)| 00:00:01 |
|* 23 | VIEW | | 11 | 682 | 2 (0)| 00:00:01 |
| 24 | TABLE ACCESS FULL | SYS_TEMP_0FD9DBAF1_9261CF31 | 11 | 2343 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("BAU"."ASAC01FK_A_ID"="SANW"."ASAC01FK_ID"||'' AND
"BAU"."ASAC01FK_B_FUNC"="SANW"."ASAC01FK_B_FUNC" AND
"BAU"."ASAC01FK_S_TYPE"="SANW"."ASAC01FK_S_TYPE" AND
"BAU"."ASAC01FK_TYPE_CODE"="SANW"."ASAC01FK_A_TYPE" AND
"SANW"."ASAC01FK_B_FUNC"="CNR"."B_FUNC_CD")
4 - access("BAU"."R_NAME"="CNR"."R_NAME" AND
"BAU"."BAAC02FK_CURR_CD"="CNR"."C_CD" AND
"BAU"."ASAC01FK_B_FUNC"="CNR"."B_FUNC_CD")
5 - access("CNR"."FM_CODE"='SP')
7 - filter("SANW"."EFF_STATUS_CODE"='A')
10 - filter("B"."PRIMARY_ACCT_IND"='N' OR "B"."PRIMARY_ACCT_IND"='Y')
13 - filter("D_RANK"=1)
16 - access("PS"."C_CD"="P"."C_CD" AND "PS"."C_ID_2"="P"."C_ID_2" AND
"PS"."CF_ID_2"="P"."CF_ID_2" AND "PS"."B_FUNC_2"="P"."B_FUNC_2" AND
"PS"."S_TYPE_2"="P"."S_TYPE_2" AND "PS"."A_TYPE_2"="P"."A_TYPE_2")
17 - filter("PS"."D_RANK"<>1)
23 - filter("B"."PRIMARY_ACCT_IND"=' ')