I have query something like
WITH
str_table as (
SELECT stringtext, stringnumberid
FROM STRING_TABLE
WHERE LANGID IN (23,62)
),
data as (
select *
from employee emp
left outer join str_table st on emp.nameid = st.stringnumberid
)
select * from data
I know With clause will work in this manner
Step 1 : The SQL Query within the with clause is executed at first step.
Step 2 : The output of the SQL query is stored into temporary relation of with clause.
Step 3 : The Main query is executed with temporary relation produced at the last stage.
Now I want to ask whether the indexes created on the actual STRING_TABLE are going to help in temporary str_table produce by the With clause? I want to ask whether the indexes also have impact on str_table or not?
Oracle will not process CTE one by one. It will analyze the SQL as a whole. Your SQL is most likely the same as following in the eye of Oracle optimizer
select emp.*
from employee emp left outer join STRING_TABLE st
on emp.nameid = st.stringnumberid
where st.LANGID IN (23,62);
Oracle can use index on STRING_TABLE. Whether it will depends on the table statistics. For example, if the table has few rows (say a few hundred), Oracle will likely not use index.
It depends.
First of all, with clause is not a temporary table. As documentation says:
Oracle Database optimizes the query by treating the query name as either an inline view or as a temporary table.
Optimizer decides to materialize with subquery if either you forse it to do so by using /*+materialize*/ hint inside the subquery or you reuse this with subquery more than once.
In the example below Oracle uses with clause as inline view and merges it within the main query:
explain plan for
with a as (
select
s.textid,
s.textvalue,
a.id,
a.other_column
from string_table s
join another_tab a
on s.textid = a.textid
where langid in (1)
)
select *
from big_table b
join a a_name
on b.name_textid = a_name.textid
and b.job_textid = a_name.id
| PLAN_TABLE_OUTPUT |
| :----------------------------------------------------------------------------------- |
| Plan hash value: 1854049435 |
| |
| ------------------------------------------------------------------------------------ |
| | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | |
| ------------------------------------------------------------------------------------ |
| | 0 | SELECT STATEMENT | | 1 | 1147 | 74 (0)| 00:00:01 | |
| |* 1 | HASH JOIN | | 1 | 1147 | 74 (0)| 00:00:01 | |
| | 2 | TABLE ACCESS FULL | ANOTHER_TAB | 39 | 3042 | 3 (0)| 00:00:01 | |
| |* 3 | HASH JOIN | | 31 | 33139 | 71 (0)| 00:00:01 | |
| | 4 | TABLE ACCESS FULL| BIG_TABLE | 19 | 10279 | 3 (0)| 00:00:01 | |
| |* 5 | TABLE ACCESS FULL| STRING_TABLE | 1143 | 589K| 68 (0)| 00:00:01 | |
| ------------------------------------------------------------------------------------ |
But depending on the statistics and hints it may evaluate subquery first and then add it to the main query:
explain plan for
with a as (
select
s.textid,
s.textvalue,
a.id,
a.other_column
from string_table s
join another_tab a
on s.textid = a.textid
where langid in (1)
)
select /*+NO_MERGE(a_name)*/ *
from big_table b
join a a_name
on b.name_textid = a_name.textid
and b.job_textid = a_name.id
| PLAN_TABLE_OUTPUT |
| :------------------------------------------------------------------------------------ |
| Plan hash value: 4105667421 |
| |
| ------------------------------------------------------------------------------------- |
| | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | |
| ------------------------------------------------------------------------------------- |
| | 0 | SELECT STATEMENT | | 101 | 110K| 74 (0)| 00:00:01 | |
| |* 1 | HASH JOIN | | 101 | 110K| 74 (0)| 00:00:01 | |
| | 2 | TABLE ACCESS FULL | BIG_TABLE | 19 | 10279 | 3 (0)| 00:00:01 | |
| | 3 | VIEW | | 64 | 37120 | 71 (0)| 00:00:01 | |
| |* 4 | HASH JOIN | | 64 | 38784 | 71 (0)| 00:00:01 | |
| | 5 | TABLE ACCESS FULL| ANOTHER_TAB | 39 | 3042 | 3 (0)| 00:00:01 | |
| |* 6 | TABLE ACCESS FULL| STRING_TABLE | 1143 | 589K| 68 (0)| 00:00:01 | |
| ------------------------------------------------------------------------------------- |
When you use with subquery twice, optimizer decides to materialize it:
explain plan for
with a as (
select
s.textid,
s.textvalue
from string_table s
where langid in (1)
)
select *
from big_table b
join a a_name
on b.name_textid = a_name.textid
join a a_job
on b.job_textid = a_job.textid
| PLAN_TABLE_OUTPUT |
| :--------------------------------------------------------------------------------------------------------------------- |
| Plan hash value: 1371454296 |
| |
| ---------------------------------------------------------------------------------------------------------------------- |
| | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | |
| ---------------------------------------------------------------------------------------------------------------------- |
| | 0 | SELECT STATEMENT | | 63 | 98973 | 67 (0)| 00:00:01 | |
| | 1 | TEMP TABLE TRANSFORMATION | | | | | | |
| | 2 | LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D7224_469C01 | | | | | |
| | 3 | TABLE ACCESS BY INDEX ROWID BATCHED | STRING_TABLE | 999 | 515K| 22 (0)| 00:00:01 | |
| |* 4 | INDEX RANGE SCAN | IX | 999 | | 4 (0)| 00:00:01 | |
| |* 5 | HASH JOIN | | 63 | 98973 | 45 (0)| 00:00:01 | |
| |* 6 | HASH JOIN | | 35 | 36960 | 24 (0)| 00:00:01 | |
| | 7 | TABLE ACCESS FULL | BIG_TABLE | 19 | 10279 | 3 (0)| 00:00:01 | |
| | 8 | VIEW | | 999 | 502K| 21 (0)| 00:00:01 | |
| | 9 | TABLE ACCESS FULL | SYS_TEMP_0FD9D7224_469C01 | 999 | 502K| 21 (0)| 00:00:01 | |
| | 10 | VIEW | | 999 | 502K| 21 (0)| 00:00:01 | |
| | 11 | TABLE ACCESS FULL | SYS_TEMP_0FD9D7224_469C01 | 999 | 502K| 21 (0)| 00:00:01 | |
| ---------------------------------------------------------------------------------------------------------------------- |
So when there are some indexes on tables inside with subquery they may be used in all above cases: before materialization, when subquery is not merged and when subquery is merged and some idexes provide better query plan on merged subquery (even when those indexes are not used when you execute subquery alone).
What about idexes: if they provide high selectivity (i.e. number of rows retrieved by index is small compared to the overall number of rows), then Oracle will consider to use it. Note, that index access has two steps: read index blocks and then read table blocks that contain rowids found by index. If table size is not much bigger than index size, then Oracle may use table scan instead of index scan even for quite selective predicate (because of doubled IO).
In the below example I've used "small" texts (100 chars) and big_table table of 20 rows and this index for text table:
create index ix
on string_table(langid, textid)
Optimizer decides to use index range scan and read only blocks of the first level (first column of the index):
explain plan for
with a as (
select
s.textid,
s.textvalue
from string_table s
where langid in (1)
)
select *
from big_table b
join a a_name
on b.name_textid = a_name.textid
| PLAN_TABLE_OUTPUT |
| :---------------------------------------------------------------------------------------------------- |
| Plan hash value: 1660330381 |
| |
| ----------------------------------------------------------------------------------------------------- |
| | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | |
| ----------------------------------------------------------------------------------------------------- |
| | 0 | SELECT STATEMENT | | 29 | 31001 | 26 (0)| 00:00:01 | |
| |* 1 | HASH JOIN | | 29 | 31001 | 26 (0)| 00:00:01 | |
| | 2 | TABLE ACCESS FULL | BIG_TABLE | 19 | 10279 | 3 (0)| 00:00:01 | |
| | 3 | TABLE ACCESS BY INDEX ROWID BATCHED| STRING_TABLE | 999 | 515K| 23 (0)| 00:00:01 | |
| |* 4 | INDEX RANGE SCAN | IX | 999 | | 4 (0)| 00:00:01 | |
| ----------------------------------------------------------------------------------------------------- |
| |
| Predicate Information (identified by operation id): |
| --------------------------------------------------- |
| |
| 1 - access("B"."NAME_TEXTID"="S"."TEXTID") |
| 4 - access("LANGID"=1) | |
But when we reduce the number of rows in big_table, it uses both the columns for index scan:
delete from big_table
where id > 4
explain plan for
with a as (
select
s.textid,
s.textvalue
from string_table s
where langid in (1)
)
select *
from big_table b
join a a_name
on b.name_textid = a_name.textid
| PLAN_TABLE_OUTPUT |
| :-------------------------------------------------------------------------------------------- |
| Plan hash value: 1766926914 |
| |
| --------------------------------------------------------------------------------------------- |
| | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | |
| --------------------------------------------------------------------------------------------- |
| | 0 | SELECT STATEMENT | | 6 | 18216 | 11 (0)| 00:00:01 | |
| | 1 | NESTED LOOPS | | 6 | 18216 | 11 (0)| 00:00:01 | |
| | 2 | NESTED LOOPS | | 6 | 18216 | 11 (0)| 00:00:01 | |
| | 3 | TABLE ACCESS FULL | BIG_TABLE | 4 | 4032 | 3 (0)| 00:00:01 | |
| |* 4 | INDEX RANGE SCAN | IX | 1 | | 1 (0)| 00:00:01 | |
| | 5 | TABLE ACCESS BY INDEX ROWID| STRING_TABLE | 2 | 4056 | 2 (0)| 00:00:01 | |
| --------------------------------------------------------------------------------------------- |
| |
| Predicate Information (identified by operation id): |
| --------------------------------------------------- |
| |
| 4 - access("LANGID"=1 AND "B"."NAME_TEXTID"="S"."TEXTID") |
| |
You may check above code snippets in the db<>fiddle.
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'm having a hard time optimising a sql query that takes about 1 min to complete. Here is the query :
SELECT mpg.ID_PROD_GARN, mpg.NO_PROD
FROM ACO.prime p
JOIN ACO.facture_compt fc
ON fc.id_factr = p.id_factr
JOIN ACO.V_CONTRAT vc
ON vc.NO_POLC = p.NO_POLC
JOIN ACO.MV_PRODUIT mp
ON mp.NO_PROD =vc.NO_PROD
JOIN ACO.MV_PROD_GARN mpg
ON mpg.NO_PROD = mp.NO_PROD
WHERE p.id_prime =
( SELECT MAX(id_prime) AS prime FROM ACO.prime p WHERE p.no_polc='T3167978')
AND mpg.ID_PROD_GARN = '1238'
AND fc.cd_stat_factr = 'comp';
V_CONTRAT is a view and (if my understanding is correct) when joining the view in this way SQL is running trough all the rows to find the result. I did a bit of research and found that indexing this view could speed up my query. So :
CREATE INDEX indx_no_produit ON ACO.V_CONTRAT(NO_PROD);
Unfortunately I get an error saying that I can't index a view SQL : ORA-01702 : you can't use that here.
*Cause: Among other possible causes, this message will be produced if an
attempt was made to define an Editioning View over a view.
*Action: An Editioning View may only be created over a base table.
So my question is how could I speed up this query elegantly?
Many thanks in advance!
Edit 1 : here is the explained plan
Plan hash value: 3107129748
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 102 | | 543K (1)| 00:00:22 |
| 1 | NESTED LOOPS | | 1 | 102 | | 543K (1)| 00:00:22 |
| 2 | NESTED LOOPS | | 1 | 90 | | 543K (1)| 00:00:22 |
| 3 | NESTED LOOPS | | 1 | 35 | | 3 (0)| 00:00:01 |
| 4 | NESTED LOOPS | | 1 | 14 | | 2 (0)| 00:00:01 |
| 5 | MAT_VIEW ACCESS BY INDEX ROWID| MV_PROD_GARN | 1 | 9 | | 1 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | PK_PROG | 1 | | | 1 (0)| 00:00:01 |
| 7 | BITMAP CONVERSION TO ROWIDS | | 1 | 5 | | 1 (0)| 00:00:01 |
|* 8 | BITMAP INDEX FAST FULL SCAN | MV_PROD_NO_PROD_IDX | | | | | |
| 9 | TABLE ACCESS BY INDEX ROWID | PRIME | 1 | 21 | | 1 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | PK_PRIME | 1 | | | 1 (0)| 00:00:01 |
| 11 | SORT AGGREGATE | | 1 | 15 | | | |
| 12 | FIRST ROW | | 1 | 15 | | 1 (0)| 00:00:01 |
|* 13 | INDEX RANGE SCAN (MIN/MAX) | PRIME_NO_POLC_IDX | 1 | 15 | | 1 (0)| 00:00:01 |
|* 14 | VIEW | V_CONTRAT | 1 | 55 | | 543K (1)| 00:00:22 |
| 15 | SORT UNIQUE | | 16M| 5011M| 2740M| 543K (1)| 00:00:22 |
| 16 | UNION-ALL | | | | | | |
|* 17 | HASH JOIN | | 16M| 2502M| | 55963 (4)| 00:00:03 |
| 18 | VIEW | index$_join$_016 | 103 | 1339 | | 2 (0)| 00:00:01 |
|* 19 | HASH JOIN | | | | | | |
| 20 | INDEX FAST FULL SCAN | PK_PROD | 103 | 1339 | | 1 (0)| 00:00:01 |
| 21 | INDEX FAST FULL SCAN | PRODUIT_COMBINE_IDX | 103 | 1339 | | 1 (0)| 00:00:01 |
|* 22 | HASH JOIN RIGHT OUTER | | 16M| 2293M| 261M| 55860 (4)| 00:00:03 |
| 23 | INDEX FAST FULL SCAN | ROL_INDEX1 | 10M| 145M| | 5703 (2)| 00:00:01 |
|* 24 | HASH JOIN RIGHT OUTER | | 6751K| 824M| 117M| 44540 (4)| 00:00:02 |
| 25 | INLIST ITERATOR | | | | | | |
|* 26 | INDEX RANGE SCAN | ROL_CDROL_NOINTR_IDX | 3975K| 72M| | 756 (2)| 00:00:01 |
|* 27 | HASH JOIN RIGHT OUTER | | 4192K| 435M| 90M| 40898 (3)| 00:00:02 |
|* 28 | TABLE ACCESS FULL | ROLE | 2881K| 57M| | 14553 (4)| 00:00:01 |
|* 29 | HASH JOIN | | 2941K| 246M| 104M| 24558 (3)| 00:00:01 |
| 30 | INDEX FAST FULL SCAN | INFO_BASE_DISTRIBUTEUR_FK1 | 4047K| 57M| | 1925 (2)| 00:00:01 |
|* 31 | HASH JOIN | | 2961K| 206M| 136M| 20967 (3)| 00:00:01 |
| 32 | TABLE ACCESS FULL | CONTRAT_ITER | 4088K| 89M| | 12159 (2)| 00:00:01 |
|* 33 | HASH JOIN RIGHT OUTER | | 2961K| 141M| 32M| 7292 (3)| 00:00:01 |
|* 34 | INDEX RANGE SCAN | ROL_CDROL_NOINTR_IDX | 933K| 22M| | 890 (1)| 00:00:01 |
|* 35 | TABLE ACCESS FULL | CONTRAT | 2615K| 62M| | 5781 (3)| 00:00:01 |
|* 36 | HASH JOIN OUTER | | 29239 | 2912K| | 10228 (3)| 00:00:01 |
|* 37 | HASH JOIN | | 29239 | 1941K| | 285 (2)| 00:00:01 |
| 38 | TABLE ACCESS FULL | DISTRIBUTEUR | 9142 | 91420 | | 45 (3)| 00:00:01 |
|* 39 | HASH JOIN | | 29239 | 1656K| | 240 (1)| 00:00:01 |
| 40 | INDEX FULL SCAN | PRODUIT_COMBINE_IDX | 103 | 1030 | | 1 (0)| 00:00:01 |
|* 41 | TABLE ACCESS FULL | TPA_CONTRAT_MENSUEL | 29239 | 1370K| | 239 (1)| 00:00:01 |
| 42 | TABLE ACCESS FULL | COUNTERPARTY | 3547K| 115M| | 9921 (2)| 00:00:01 |
|* 43 | INDEX RANGE SCAN | FACTURE_COMPT_INDEX9 | 1 | 12 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("MPG"."ID_PROD_GARN"=1238)
8 - filter("MPG"."NO_PROD"="MP"."NO_PROD")
10 - access("P"."ID_PRIME"= (SELECT MAX("ID_PRIME") FROM "ACO"."PRIME" "P" WHERE "P"."NO_POLC"='T3167978'))
13 - access("P"."NO_POLC"='T3167978')
14 - filter("VC"."NO_POLC"="P"."NO_POLC" AND "MP"."NO_PROD"="VC"."NO_PROD")
17 - access("PROD"."NO_PROD"="ITER"."NO_PROD" AND "PROD"."NO_VERS_PROD"="ITER"."NO_VERS_PROD")
19 - access(ROWID=ROWID)
22 - access("ROLAUTRE"."ID_CONT"(+)="CONT"."ID_CONT" AND "ROLAUTRE"."NO_ITER_CONT"(+)="CONT"."NO_DERN_ITER")
24 - access("ROLA"."ID_CONT"(+)="CONT"."ID_CONT" AND "ROLA"."NO_ITER_CONT"(+)="CONT"."NO_DERN_ITER")
26 - access("ROLA"."CD_ROLE"(+)='ap' OR "ROLA"."CD_ROLE"(+)='debit' OR "ROLA"."CD_ROLE"(+)='emprun')
27 - access("ROLPAY"."ID_CONT"(+)="CONT"."ID_CONT" AND "ROLPAY"."NO_ITER_CONT"(+)="CONT"."NO_DERN_ITER")
28 - filter("ROLPAY"."CD_ROLE"(+)='pay' AND "ROLPAY"."IND_PAY_PRI"(+)='1')
29 - access("ITER"."ID_INFO_BASE"="IB"."ID_INFO_BASE" AND "ITER"."NO_ITER_CONT"="IB"."NO_ITER_CONT" AND
SYS_OP_DESCEND("ITER"."NO_ITER_CONT")=SYS_OP_DESCEND("IB"."NO_ITER_CONT"))
31 - access("ITER"."ID_CONT"="CONT"."ID_CONT" AND "ITER"."NO_ITER_CONT"="CONT"."NO_DERN_ITER" AND
SYS_OP_DESCEND("ITER"."NO_ITER_CONT")=SYS_OP_DESCEND("CONT"."NO_DERN_ITER"))
33 - access("ROLP"."ID_CONT"(+)="CONT"."ID_CONT" AND "ROLP"."NO_ITER_CONT"(+)="CONT"."NO_DERN_ITER")
34 - access("ROLP"."CD_ROLE"(+)='pren')
35 - filter("CONT"."NO_SEQ_PROPO_SEL"=0)
36 - access("CTRPY"."LAST_NAME"(+)="TCM"."CON_NOM_ASS_PRINC" AND
"CTRPY"."FRST_NAME"(+)="TCM"."CON_PRENOM_ASS_PRINC" AND "CTRPY"."DT_BIRTH"(+)="TCM"."CON_DATE_NAISS_ASS_PRINC")
37 - access("TCM"."CON_NO_DISTRIBUTEUR"="A"."NO_DIST")
39 - access("PROD"."NO_PROD"="TCM"."CON_CODE_PRODDUIT")
41 - filter("TCM"."CON_VERSION_CONTRAT"=1)
43 - access("FC"."ID_FACTR"="P"."ID_FACTR" AND "FC"."CD_STAT_FACTR"='comp')
Edit 2 : Here is the view V_CONTRAT
SELECT DISTINCT cont.ID_CONT,
cont.NO_POLC,
cont.cd_divs,
prod.no_prod,
prod.cd_cie_encai,
prod.cd_faml_cptb,
ib.no_dist_init,
CASE
WHEN ROLP.ID_ROLE IS NOT NULL THEN ROLP.NO_INTR
WHEN rola.no_intr IS NOT NULL THEN rola.no_intr
WHEN rolpay.no_intr IS NOT NULL THEN rolpay.no_intr
ELSE rolAutre.no_intr
END
AS NO_INTR_PRINC,
rolpay.no_intr AS NO_INTR_PAY
FROM VIRAGE.CONTRAT CONT
INNER JOIN
VIRAGE.contrat_iter iter
ON iter.id_cont = cont.id_cont
AND ITER.NO_ITER_CONT = CONT.NO_DERN_ITER
AND CONT.NO_SEQ_PROPO_SEL = 0
INNER JOIN
VIRAGE.info_base ib
ON iter.id_info_base = ib.id_info_base
AND iter.no_iter_cont = ib.no_iter_cont
INNER JOIN
VIRAGE.produit prod
ON prod.no_prod = iter.no_prod
AND prod.no_vers_prod = iter.no_vers_prod
LEFT JOIN
VIRAGE.role rolp
ON rolp.id_cont = cont.id_cont
AND rolp.no_iter_cont = cont.no_dern_iter
AND rolp.cd_role = 'pren'
LEFT JOIN
VIRAGE.role rola
ON rola.id_cont = cont.id_cont
AND rola.no_iter_cont = cont.no_dern_iter
AND ( rola.cd_role = 'ap'
OR ROLA.CD_ROLE = 'debit'
OR rola.cd_role = 'emprun')
LEFT JOIN
VIRAGE.role rolpay
ON rolpay.id_cont = cont.id_cont
AND rolpay.no_iter_cont = cont.no_dern_iter
AND rolpay.cd_role = 'pay'
AND rolpay.ind_pay_pri = '1'
LEFT JOIN
VIRAGE.role rolAutre
ON rolAutre.id_cont = cont.id_cont
AND rolAutre.no_iter_cont = cont.no_dern_iter
UNION
SELECT DISTINCT
CAST (tcm.con_sequence AS NUMBER (10, 0)) AS id_cont,
CAST (tcm.con_numero_contrat AS VARCHAR2 (20)) AS no_polc,
CAST (vd.cd_divs_compt AS VARCHAR2 (11)) AS cd_divs,
CAST (tcm.con_code_prodduit AS NUMBER (5, 0)) AS no_prod,
CAST (prod.cd_cie_encai AS VARCHAR2 (1)) AS cd_cie_encai,
CAST (prod.cd_faml_cptb AS VARCHAR2 (11)) AS cd_faml_cptb,
CAST (tcm.con_no_distributeur AS VARCHAR2 (15)) AS no_dist_init,
CAST (ctrpy.no_ctrpy AS NUMBER) AS no_intr_princ,
CAST (ctrpy.no_ctrpy AS NUMBER) AS no_intr_pay
FROM TPA.TPA_CONTRAT_MENSUEL tcm
INNER JOIN
VIRAGE.produit prod
ON prod.no_prod = tcm.con_code_prodduit
LEFT JOIN
counterparty ctrpy
ON ctrpy.last_name = tcm.con_nom_ass_princ
AND ctrpy.frst_name = tcm.con_prenom_ass_princ
AND ctrpy.dt_birth = tcm.con_date_naiss_ass_princ
INNER JOIN
v_distributeur vd
ON tcm.con_no_distributeur = vd.no_dist
WHERE TCM.CON_VERSION_CONTRAT=1
Indexes can only be created in tables.
Make sure you create an index for every single foreign key in every table. Oracle does not create them by default as other systems do.
Check also the performance of the sub-query and if it takes too long you should also create indexes for every attribute in the WHERE clause.
Don't forget that indexes need maintenance.
I have some issue with cte, if I use this query oracle materialized the cte1 view and query will be slow
with cte1 as (..),
cte2 as ( ... use cte1 ...),
cte3 as ( ... use cte1 ...)
select * from cte2 join cte3
on ...
in the following query Oracle does not materialize cte1 and the query is 20 times quicker as before:
with cte1 as (..),
cte2 as ( ... use cte1 ...)
select * from cte2 on ....
as well
with cte1 as (..),
cte3 as ( ... use cte1 ...)
select * from cte3 on ....
Is it possible to force Oracle not to materialize CTE so it will be using idexes ?
Execution plan for query 1:
Plan hash value: 1038428573
--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 126K| 104M| 1753 (1)| 00:00:22 |
| 1 | TEMP TABLE TRANSFORMATION | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9DC639_11293183 | | | | |
|* 3 | HASH JOIN | | 39285 | 1726K| 1618 (1)| 00:00:20 |
|* 4 | HASH JOIN | | 31724 | 650K| 863 (1)| 00:00:11 |
|* 5 | INDEX RANGE SCAN | UQ1_xxxxxxx | 31724 | 402K| 23 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL | xxxx | 384K| 3005K| 837 (1)| 00:00:11 |
| 7 | TABLE ACCESS FULL | xxxxxxxxx | 481K| 11M| 753 (1)| 00:00:10 |
|* 8 | HASH JOIN | | 126K| 104M| 136 (3)| 00:00:02 |
|* 9 | HASH JOIN | | 3 | 1314 | 68 (2)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID| xxxxxxxxxxxxxxxx | 2 | 20 | 1 (0)| 00:00:01 |
|* 11 | INDEX RANGE SCAN | FK2_xxxxxxxxxxxxxxxx | 2 | | 1 (0)| 00:00:01 |
|* 12 | VIEW | | 39285 | 16M| 66 (0)| 00:00:01 |
| 13 | TABLE ACCESS FULL | SYS_TEMP_0FD9DC639_11293183 | 39285 | 1035K| 66 (0)| 00:00:01 |
|* 14 | VIEW | | 39285 | 16M| 66 (0)| 00:00:01 |
| 15 | TABLE ACCESS FULL | SYS_TEMP_0FD9DC639_11293183 | 39285 | 1035K| 66 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ES"."xxxxxxxxxxxxx"="E"."xxxxxxxxxxxxxx")
4 - access("CR"."xxxxxxxxxxxxxx"="E"."xxxxxxxxxxxxID")
5 - access("CR"."xxxxxxxID"=TO_NUMBER(:xxxxxxxID))
8 - access("EV"."xxxxxxxxxxxx_ID"="LA"."xxxxxx_ID")
9 - access("LA"."xxxxxxxxxxxxx_ID"="EV2"."xxxxxxxxxxxx_ID")
11 - access("LA"."xxxxxxxID"=359134)
12 - filter("EV2"."xxxxxxxxxxxxxxxxID"=4)
14 - filter("EV"."xxxxxxxxxxxxxxx_ID"=3 AND "EV"."xxxxxxxxxxxx_ID"=359134)
Execution plan for query 2:
Plan hash value: 1937334873
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 55 | 4 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 1 | 55 | 4 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 1 | 31 | 3 (0)| 00:00:01 |
| 4 | NESTED LOOPS | | 2 | 46 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| xxxxxxxxxxxxxxxx | 2 | 20 | 1 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | FK2_xxxxxxxxxxxxxxxx | 2 | | 1 (0)| 00:00:01 |
|* 7 | TABLE ACCESS BY INDEX ROWID| xxxxxxxxxxxx | 1 | 13 | 1 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | FK2_xxxxxxxxxxxx | 4 | | 1 (0)| 00:00:01 |
|* 9 | TABLE ACCESS BY INDEX ROWID | xxxxxxxxxxxxx | 1 | 8 | 1 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | PK_xxxxxxxxxxxxx | 1 | | 1 (0)| 00:00:01 |
|* 11 | INDEX RANGE SCAN | UQ1_xxxxxxxxxxxxxxxxxx | 1 | | 1 (0)| 00:00:01 |
| 12 | TABLE ACCESS BY INDEX ROWID | xxxxxxxxxxxxxxxxxx | 1 | 24 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("LA"."xxxxxx_ID"=359134)
7 - filter("CR"."xxxxxxxID"=TO_NUMBER(:xxxxxxxID))
8 - access("LA"."xxxxxxxxxxxxx_ID"="CR"."xxxxxxxxxxxx_ID")
9 - filter("E"."xxxxxxxxxxxxxxx_ID"=4)
10 - access("CR"."xxxxxxxxxxxxID"="E"."xxxxxxxxxxxxID")
11 - access("ES"."xxxxxxxxxxxID"="E"."xxxxxxxxxxxxID")
There's an undocumented /*+ inline */ hint that should prevent the optimizer from materializing the CTE. Should be placed right after the select in the CTE itself.
The /*+ materialize */ hint would be the opposite of that, i.e. request that the view be materialized.
Neither of these are officially documented as far as I can tell, so use with caution. Opening an SR with Oracle support to get some advice is a good idea in this sort of case, they could "approve" the use of the hint or provide alternatives (including potential patches/bugfixes that would address the issue).
+1'd question and Mat's answer.
Although there is another reason for Oracle to do TEMP TABLE TRANSFORMATION.
If you have set init parameter star_transformation_enabled to TRUE.
If it's not desired in your case, you can switch it off by running
alter session set star_transformation_enabled=TEMP_DISABLE;
This will keep star tranformation enabled, but will switch off TEMP TABLE TRANSFORMATION.
https://blogs.oracle.com/optimizer/entry/star_transformation
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.