Retrieving numbers in ranges via Oracle SQL - sql

I have a very slow query due to scanning through millions of records. The query searches for how many numbers are in a specific range.
I have 2 tables: numbers_in_ranges and person table
Create table numbers_in_ranges
( range_id number(9,0) ,
begin_range number(9,0),
end_range number(9,0)
) ;
Create table person
(
id integer,
a_number varchar(9),
first_name varchar(25),
last_name varchar(25)
);
Data for numbers_in_ranges
range_id| begin_range | end_range
--------|------------------------
101 | 100000000 | 200000000
102 | 210000000 | 290000000
103 | 350000000 | 459999999
104 | 461000000 | 569999999
106 | 241000000 | 241999999
e.t.c.
Data for person
id | a_number | first_name | last_name
---|------------|------------|-----------
1 | 100000001 | Maria | Doe
2 | 100000999 | Emily | Davis
3 | 150000000 | Dave | Smith
4 | 461000000 | Jane | Jones
6 | 241000001 | John | Doe
7 | 100000002 | Maria | Doe
8 | 100009999 | Emily | Davis
9 | 150000010 | Dave | Smith
10 | 210000001 | Jane | Jones
11 | 210000010 | John | Doe
12 | 281000000 | Jane | Jones
13 | 241000000 | John | Doe
14 | 460000001 | Maria | Doe
15 | 500000999 | Emily | Davis
16 | 550000010 | Dave | Smith
17 | 461000010 | Jane | Jones
18 | 241000020 | John | Doe
e.t.c.
We are getting the range data from a remote database via a database link and storing it in a materialized view.
The query
select nums.range_id, count(p. a_number) as a_count
from number_in_ranges nums
left join person p on to_number(p. a_number)
between nums.begin_range and nums.end_range
group by nums.range_id;
The result looks like
range_id| a_count
--------|------------------------
101 | 6
102 | 5
103 | 2
104 | 3
e.t.c
As I said, this query is very slow.
Here is the explain plan
Plan hash value: 3785994407
---------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
---------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9352 | 264K| | 42601 (31)| 00:00:02 | | | |
| 1 | PX COORDINATOR | | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10002 | 9352 | 264K| | 42601 (31)| 00:00:02 | Q1,02 | P->S | QC (RAND) |
| 3 | HASH GROUP BY | | 9352 | 264K| | 42601 (31)| 00:00:02 | Q1,02 | PCWP | |
| 4 | PX RECEIVE | | 9352 | 264K| | 42601 (31)| 00:00:02 | Q1,02 | PCWP | |
| 5 | PX SEND HASH | :TQ10001 | 9352 | 264K| | 42601 (31)| 00:00:02 | Q1,01 | P->P | HASH |
| 6 | HASH GROUP BY | | 9352 | 264K| | 42601 (31)| 00:00:02 | Q1,01 | PCWP | |
| 7 | MERGE JOIN OUTER | | 2084M| 56G| | 37793 (23)| 00:00:02 | Q1,01 | PCWP | |
| 8 | SORT JOIN | | 9352 | 173K| | 3 (34)| 00:00:01 | Q1,01 | PCWP | |
| 9 | PX BLOCK ITERATOR | | 9352 | 173K| | 2 (0)| 00:00:01 | Q1,01 | PCWC | |
| 10 | MAT_VIEW ACCESS FULL | NUMBERS_IN_RANGES | 9352 | 173K| | 2 (0)| 00:00:01 | Q1,01 | PCWP | |
|* 11 | FILTER | | | | | | | Q1,01 | PCWP | |
|* 12 | SORT JOIN | | 89M| 850M| 2732M| 29681 (1)| 00:00:02 | Q1,01 | PCWP | |
| 13 | BUFFER SORT | | | | | | | Q1,01 | PCWC | |
| 14 | PX RECEIVE | | 89M| 850M| | 4944 (1)| 00:00:01 | Q1,01 | PCWP | |
| 15 | PX SEND BROADCAST | :TQ10000 | 89M| 850M| | 4944 (1)| 00:00:01 | Q1,00 | P->P | BROADCAST |
| 16 | PX BLOCK ITERATOR | | 89M| 850M| | 4944 (1)| 00:00:01 | Q1,00 | PCWC | |
| 17 | INDEX FAST FULL SCAN| PERSON_AN_IDX | 89M| 850M| | 4944 (1)| 00:00:01 | Q1,00 | PCWP | |
---------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
11 - filter("NUMS"."END_RANGE">=TO_NUMBER("P"."A_NUMBER"(+)))
12 - access("NUMS"."BEGIN_RANGE"<=TO_NUMBER("P"."A_NUMBER"(+)))
filter("NUMS"."BEGIN_RANGE"<=TO_NUMBER("P"."A_NUMBER"(+)))
Note
-----
- automatic DOP: Computed Degree of Parallelism is 16 because of degree limit
I tried to run the deltas for the month and then append them to the table, like:
if new range_id is found then insert
if range_id is found then update
So we don't have to scan the whole table.
But this solution didn't work because some ranges are updated, and splicing happens, for example:
We create a new range_id = 110 with ranges between 100110000 and 210000001
then range_id = 101 is spliced to 100000000 and 100110000
and range_id = 102 is spliced to 100110001 and 210000000 ;
Now I thought of creating a trigger for when a new range is created or updated to update that table; however, that is impossible since we are getting this data from a remote database that stores the data into a Materialized View, and we cannot put a trigger on a read-only materialized view.
My question is there any other way that I can do this or optimize this query?
Thank you!

The issue is that Oracle tries to broadcast the table with all ID's that looks quite strange for this case.
However, since you need only to count rows and (it looks like) the intervals do not overlap, you may improve the performance and avoid join of two datasets using a trick: transform the data to event stream where each start and end value identifies the beginning and end of series and then count the number of events in this series. This way you may use match_recognize which is dramatically faster than join.
The query will be:
with ranges_unpivot as (
/*Transform from_ ... to_... to the event-like structure*/
select
id
, val
, val_type
from ranges_table
unpivot(
val for val_type in (from_num as '01_START', to_num as '03_END')
)
union all
/*Append the rest of the data to the event stream*/
select
null,
id,
/*
This should be ordered between START mark and END mark
to process edge cases correctly
*/
'02_val'
from other_table
where id <= (select max(to_num) from ranges_table)
)
select /*+parallel(4) gather_plan_statistics*/ *
from ranges_unpivot
match_recognize (
order by val asc, val_type asc
measures
start_.id as range_id,
count(values_.val) as count_
pattern (start_ values_* end_)
define
start_ as val_type = '01_START',
values_ as val_type = '02_val',
end_ as val_type = '03_END'
)
which shows this time in the query plan:
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.33
Compared to join query:
select /*+gather_plan_statistics*/
rt.id as range_id,
count(ot.id) as count_
from ranges_table rt
left join other_table ot
on rt.from_num <= ot.id
and rt.to_num >= ot.id
group by rt.id
which shows:
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:13.84 |
See db<>fiddle.

Related

Recursive CTE much slower than CONNECT BY in Oracle

I have a table that looks like this:
Bundleref
Subbundleref
123
456
456
789
Starting from a certain reference (e.g. 123), I want a list of all descendants, all the way until the leaf values.
In Oracle, I can use a CONNECT BY clause like this:
select subbundleref from store.tw_bundles
start with bundleref = 2201114
connect by prior subbundleref = bundleref
For compatibility reasons, I am trying to convert it to a recursive CTE, like this:
WITH bundles(br,sr)
AS
(
SELECT bundleref, subbundleref
FROM store.tw_bundles where bundleref = 2201114
UNION ALL
SELECT bundleref, subbundleref
FROM store.tw_bundles twb
inner join bundles on twb.bundleref = bundles.sr
)
select sr from bundles
This gives me the same result. There is one problem though: the CONNECT BY query takes 300 ms, the recursive query takes about 50 seconds. Am I doing something inefficient here or is this not being optimized? (I'm using Oracle 19c.)
Explain plan for first query:
Plan hash value: 4216745508
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 218 | 5668 | 36 (6)| 00:00:01 |
|* 1 | CONNECT BY WITH FILTERING| | | | | |
|* 2 | INDEX RANGE SCAN | TW_BUNDLES_BUNDLEREF_IDX | 14 | 168 | 3 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 204 | 5100 | 31 (0)| 00:00:01 |
| 4 | CONNECT BY PUMP | | | | | |
|* 5 | INDEX RANGE SCAN | TW_BUNDLES_BUNDLEREF_IDX | 15 | 180 | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("BUNDLEREF"=PRIOR "SUBBUNDLEREF")
2 - access("BUNDLEREF"=2201114)
5 - access("connect$_by$_pump$_002"."prior subbundleref "="BUNDLEREF")
Note
-----
- this is an adaptive plan
And for the second one:
Plan hash value: 1467025167
-------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 259K (3)| 00:00:11 |
| 1 | SORT AGGREGATE | | 1 | | | | |
| 2 | VIEW | | 1975M| | | 259K (3)| 00:00:11 |
| 3 | UNION ALL (RECURSIVE WITH) BREADTH FIRST| | | | | | |
|* 4 | INDEX RANGE SCAN | TW_BUNDLES_BUNDLEREF_IDX | 14 | 168 | | 3 (0)| 00:00:01 |
|* 5 | HASH JOIN | | 1975M| 45G| 258M| 259K (3)| 00:00:11 |
| 6 | BUFFER SORT (REUSE) | | | | | | |
| 7 | TABLE ACCESS FULL | TW_BUNDLES | 11M| 129M| | 9208 (1)| 00:00:01 |
| 8 | RECURSIVE WITH PUMP | | | | | | |
-------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("BUNDLEREF"=2201114)
5 - access("TWB"."BUNDLEREF"="BUNDLES"."SR")

Does indexing works with "WITH" in Oracle

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.

Oracle SQL - VIEW PUSHED PREDICATE not happening when using dblink

I'm having performance issues executing the following query (Q1):
select
z_out.*,
a_out.id
from orders a_out, test z_out
where a_out.id=z_out.id and a_out.created>trunc(sysdate) and rownum<10
Table orders contains millions of rows; orders.id is the primary key and orders.craeted is indexed.
The view is:
create or replace view test as
select/*+qb_name(q_outer)*/
id,
min(value) keep (dense_rank first order by id) as value
from (
select/*+qb_name(q_inner)*/
id,
case
when substr(id, -1)<'5'
--and exists(select 1 from dual#db2)
then 'YYY'
end as attr_1
from orders a1
) a2, small_table b2
where b2.attr_1 in (nvl(a2.attr_1, '#'), '*')
group by id
where small_table b2 contains about 200 records (all the columns are varchar2).
Executing Q1 has great performances and the following execution plan:
Plan hash value: 2906430222
-----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 274 | 64 (0)| 00:00:01 | | |
|* 1 | COUNT STOPKEY | | | | | | | |
| 2 | NESTED LOOPS | | 1 | 274 | 64 (0)| 00:00:01 | | |
| 3 | PARTITION LIST ALL | | 1 | 22 | 59 (0)| 00:00:01 | 1 | 2 |
| 4 | PARTITION RANGE ALL | | 1 | 22 | 59 (0)| 00:00:01 | 1 | LAST |
| 5 | TABLE ACCESS BY LOCAL INDEX ROWID| ORDERS | 1 | 22 | 59 (0)| 00:00:01 | 1 | 29 |
|* 6 | INDEX RANGE SCAN | IDX_ORDERS_CREATED | 1 | | 57 (0)| 00:00:01 | 1 | 29 |
| 7 | VIEW PUSHED PREDICATE | TEST | 1 | 252 | 5 (0)| 00:00:01 | | |
|* 8 | FILTER | | | | | | | |
| 9 | SORT AGGREGATE | | 1 | 55 | | | | |
| 10 | NESTED LOOPS | | 259 | 14245 | 5 (0)| 00:00:01 | | |
|* 11 | INDEX UNIQUE SCAN | PK_ID | 1 | 14 | 2 (0)| 00:00:01 | | |
|* 12 | INDEX STORAGE FAST FULL SCAN | IDX_MN_AN_AD_ALL | 259 | 10619 | 3 (0)| 00:00:01 | | |
-----------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<10)
6 - access("A_OUT"."CREATED">TRUNC(SYSDATE#!))
8 - filter(COUNT(*)>0)
11 - access("ID"="A_OUT"."ID")
12 - storage("B2"."ATTR_1"=NVL(CASE WHEN SUBSTR("ID",(-1))<'5' THEN 'YYY' END ,'#') OR "B2"."ATTR_1"='*')
filter("B2"."ATTR_1"=NVL(CASE WHEN SUBSTR("ID",(-1))<'5' THEN 'YYY' END ,'#') OR "B2"."ATTR_1"='*')
Q1 performance issues happen when the line --and exists(select 1 from dual#db2) in the view is uncommented.
The new execution plan is:
Plan hash value: 3271081243
----------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | Inst |IN-OUT|
----------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 288 | 5273K (1)| 00:03:27 | | | | |
|* 1 | COUNT STOPKEY | | | | | | | | | |
|* 2 | HASH JOIN | | 1 | 288 | 5273K (1)| 00:03:27 | | | | |
| 3 | JOIN FILTER CREATE | :BF0000 | 1 | 22 | 59 (0)| 00:00:01 | | | | |
| 4 | PARTITION LIST ALL | | 1 | 22 | 59 (0)| 00:00:01 | 1 | 2 | | |
| 5 | PARTITION RANGE ALL | | 1 | 22 | 59 (0)| 00:00:01 | 1 | LAST | | |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| ORDERS | 1 | 22 | 59 (0)| 00:00:01 | 1 | 29 | | |
|* 7 | INDEX RANGE SCAN | IDX_ORDERS_CREATED | 1 | | 57 (0)| 00:00:01 | 1 | 29 | | |
| 8 | VIEW | TEST | 3840K| 974M| 5273K (1)| 00:03:27 | | | | |
| 9 | SORT GROUP BY | | 3840K| 201M| 5273K (1)| 00:03:27 | | | | |
| 10 | JOIN FILTER USE | :BF0000 | 994M| 50G| 5273K (1)| 00:03:27 | | | | |
| 11 | NESTED LOOPS | | 994M| 50G| 5273K (1)| 00:03:27 | | | | |
| 12 | INDEX FULL SCAN | PK_ID | 3840K| 51M| 66212 (1)| 00:00:03 | | | | |
|* 13 | INDEX STORAGE FAST FULL SCAN | IDX_MN_AN_AD_ALL | 259 | 10619 | 1 (0)| 00:00:01 | | | | |
| 14 | REMOTE | | | | | | | | DB2 | R->S |
----------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<10)
2 - access("A_OUT"."ID"="Z_OUT"."ID")
7 - access("A_OUT"."CREATED">TRUNC(SYSDATE#!))
13 - filter("B2"."ATTR_1"=NVL(CASE WHEN (SUBSTR("ID",(-1))<'5' AND EXISTS (SELECT 0 FROM "A1")) THEN 'YYY' END ,'#') OR
"B2"."ATTR_1"='*')
Remote SQL Information (identified by operation id):
----------------------------------------------------
14 - EXPLAIN PLAN INTO PLAN_TABLE#! FOR SELECT 0 FROM "DUAL" "A1" (accessing 'DB2' )
I would like the view to be accessed n times, like in the first scenario.
I tried using hints but didn't succeed.
May be useful to say that even with the line and exists(select 1 from dual#db2) uncommented in the view, the following query has great performances (I know that is different from Q1).
select
(select value from test z_out where a_out.id=z_out.id) as value,
a_out.id
from orders a_out
where a_out.created>trunc(sysdate) and rownum<10
So, I guess the view works fine when it's accessed n times even if the line and exists(select 1 from dual#db2) is uncommented. But I'm not being able to force the execution plan in that direction.
If hints are necessary, I'd like to add them inside the view DDL only (if possible) so that who uses the view won't have to worry about it.
================================================================
Edit: the following were executed:
alter session set statistics_level = 'ALL';
-- Q1 (the query I'm having problems with)
select * from table (dbms_xplan.display_cursor (format=>'ALLSTATS LAST'));
Plan hash value: 3271081243
------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 0 |00:00:00.01 | 0 | 0 | | | |
|* 1 | COUNT STOPKEY | | 1 | | 0 |00:00:00.01 | 0 | 0 | | | |
|* 2 | HASH JOIN | | 1 | 1 | 0 |00:00:00.01 | 0 | 0 | 3789K| 3789K| 1078K (0)|
| 3 | JOIN FILTER CREATE | :BF0000 | 1 | 1 | 25602 |00:00:00.22 | 23345 | 161 | | | |
| 4 | PARTITION LIST ALL | | 1 | 1 | 25602 |00:00:00.21 | 23345 | 161 | | | |
| 5 | PARTITION RANGE ALL | | 2 | 1 | 25602 |00:00:00.21 | 23345 | 161 | | | |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| ORDERS | 29 | 1 | 25602 |00:00:00.20 | 23345 | 161 | | | |
|* 7 | INDEX RANGE SCAN | IDX_CREATED | 13 | 1 | 25602 |00:00:00.12 | 474 | 161 | 1025K| 1025K| |
| 8 | VIEW | TEST | 1 | 3820K| 0 |00:00:00.01 | 0 | 0 | | | |
| 9 | SORT GROUP BY | | 1 | 3820K| 0 |00:00:00.01 | 0 | 0 | 73728 | 73728 | |
| 10 | JOIN FILTER USE | :BF0000 | 1 | 989M| 106M|00:03:38.87 | 60M| 52960 | | | |
| 11 | NESTED LOOPS | | 1 | 989M| 328M|00:03:04.11 | 60M| 52960 | | | |
| 12 | INDEX FULL SCAN | PK_ID | 1 | 3820K| 1245K|00:00:21.04 | 200K| 52959 | 1025K| 1025K| |
|* 13 | INDEX STORAGE FAST FULL SCAN | IDX_MN_AN_AD_ALL | 1245K| 259 | 328M|00:02:12.09 | 60M| 1 | 1025K| 1025K| |
| 14 | REMOTE | | 1 | | 1 |00:00:00.01 | 0 | 0 | | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<10)
2 - access("A_OUT"."ID"="Z_OUT"."ID")
7 - access("A_OUT"."CREATED">TRUNC(SYSDATE#!))
13 - filter(("B2"."ATTR_1"=NVL(CASE WHEN (SUBSTR("ID",(-1))<'5' AND IS NOT NULL) THEN 'YYY' END ,'#') OR "B2"."ATTR_1"='*'))
Note: Q1 performances prevent the query to complete if and exists(select 1 from dual#db2) in the view is uncommented. To get the previous execution plan I had to alter the session, run Q1, stop Q1 (after about 4 minutes) and then calculate the plan.
The following execution plan was generated the same way, but the view had the line --and exists(select 1 from dual#db2) commented (performances were good).
Plan hash value: 2906430222
-----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 9 |00:00:00.01 | 223 |
|* 1 | COUNT STOPKEY | | 1 | | 9 |00:00:00.01 | 223 |
| 2 | NESTED LOOPS | | 1 | 1 | 9 |00:00:00.01 | 223 |
| 3 | PARTITION LIST ALL | | 1 | 1 | 9 |00:00:00.01 | 41 |
| 4 | PARTITION RANGE ALL | | 1 | 1 | 9 |00:00:00.01 | 41 |
| 5 | TABLE ACCESS BY LOCAL INDEX ROWID| ORDERS | 14 | 1 | 9 |00:00:00.01 | 41 |
|* 6 | INDEX RANGE SCAN | IDX_CREATED | 12 | 1 | 9 |00:00:00.01 | 33 |
| 7 | VIEW PUSHED PREDICATE | TEST | 9 | 1 | 9 |00:00:00.01 | 182 |
|* 8 | FILTER | | 9 | | 9 |00:00:00.01 | 182 |
| 9 | SORT AGGREGATE | | 9 | 1 | 9 |00:00:00.01 | 182 |
| 10 | NESTED LOOPS | | 9 | 259 | 2376 |00:00:00.01 | 182 |
|* 11 | INDEX UNIQUE SCAN | PK_ID | 9 | 1 | 9 |00:00:00.01 | 20 |
|* 12 | INDEX STORAGE FAST FULL SCAN | IDX_MN_AN_AD_ALL | 9 | 259 | 2376 |00:00:00.01 | 162 |
-----------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<10)
6 - access("A_OUT"."CREATED">TRUNC(SYSDATE#!))
8 - filter(COUNT(*)>0)
11 - access("ID"="A_OUT"."ID")
12 - storage(("B2"."ATTR_1"=NVL(CASE WHEN SUBSTR("ID",(-1))<'5' THEN 'YYY' END ,'#') OR
"B2"."ATTR_1"='*'))
filter(("B2"."ATTR_1"=NVL(CASE WHEN SUBSTR("ID",(-1))<'5' THEN 'YYY' END ,'#') OR
"B2"."ATTR_1"='*'))

Need help in tuning Group by cube with 6 columns

I want to aggregate data from table1 in all possible combinations of the 6 columns. Table1 has 60Million rows in it. The query is running for more than an hour and still not fetching any data. Need some help in tuning this
select /*+ parallel(a,8)*/
event_date,
a.COL1,
COL2,
COL3,
COL4,
COL5,
COL6,
count (distinct id) base_count,
count (distinct case when active_flg = 'Y' then id end) unique_active_cnt,
count (case when active_flg = 'Y' then id end) active_cnt,
GROUPING_ID (a.COL1,
COL2,
COL3,
COL4,
COL5,
COL6)
AS grp_id
from table1 partition (P2017_01) a
group by event_date,
cube (a.COL1,
COL2,
COL3,
COL4,
COL5,
COL6);
Explain plan for the above sql:-
Explain complete.
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3317440810
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 392K| 81M| | 96682 (4)| 00:00:08 | | | | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | | | | | |
| 2 | PX COORDINATOR | | | | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10001 | 392K| 20M| | 1157 (5)| 00:00:01 | | | Q1,01 | P->S | QC (RAND) |
| 4 | LOAD AS SELECT (TEMP SEGMENT MERGE)| SYS_TEMP_0FDA4B738_9AA210CA | | | | | | | | Q1,01 | PCWP | |
| 5 | SORT GROUP BY ROLLUP | | 392K| 20M| 293M| 1157 (5)| 00:00:01 | | | Q1,01 | PCWP | |
| 6 | PX RECEIVE | | 4040K| 208M| | 1121 (2)| 00:00:01 | | | Q1,01 | PCWP | |
| 7 | PX SEND HASH | :TQ10000 | 4040K| 208M| | 1121 (2)| 00:00:01 | | | Q1,00 | P->P | HASH |
| 8 | PX BLOCK ITERATOR | | 4040K| 208M| | 1121 (2)| 00:00:01 | 25 | 25 | Q1,00 | PCWC | |
| 9 | TABLE ACCESS FULL | TABLE1 | 4040K| 208M| | 1121 (2)| 00:00:01 | 25 | 25 | Q1,00 | PCWP | |
| 10 | LOAD AS SELECT | SYS_TEMP_0FDA4B738_9AA210CA | | | | | | | | | | |
| 11 | PARTITION RANGE SINGLE | | 185K| 9224K| | 27366 (2)| 00:00:03 | 25 | 25 | | | |
| 12 | SORT GROUP BY ROLLUP | | 185K| 9224K| 278M| 27366 (2)| 00:00:03 | | | | | |
| 13 | TABLE ACCESS FULL | TABLE1 | 4040K| 196M| | 8081 (2)| 00:00:01 | 25 | 25 | | | |
| 14 | LOAD AS SELECT | SYS_TEMP_0FDA4B738_9AA210CA | | | | | | | | | | |
| 15 | PARTITION RANGE SINGLE | | 32686 | 1595K| | 26191 (2)| 00:00:03 | 25 | 25 | | | |
| 16 | SORT GROUP BY ROLLUP | | 32686 | 1595K| 278M| 26191 (2)| 00:00:03 | | | | | |
| 17 | TABLE ACCESS FULL | TABLE1 | 4040K| 192M| | 8081 (2)| 00:00:01 | 25 | 25 | | | |
| 18 | LOAD AS SELECT | SYS_TEMP_0FDA4B738_9AA210CA | | | | | | | | | | |
| 19 | PARTITION RANGE SINGLE | | 73 | 2336 | | 8374 (5)| 00:00:01 | 25 | 25 | | | |
| 20 | SORT GROUP BY ROLLUP | | 73 | 2336 | | 8374 (5)| 00:00:01 | | | | | |
| 21 | TABLE ACCESS FULL | TABLE1 | 4040K| 123M| | 8081 (2)| 00:00:01 | 25 | 25 | | | |
| 22 | LOAD AS SELECT | SYS_TEMP_0FDA4B738_9AA210CA | | | | | | | | | | |
| 23 | PARTITION RANGE SINGLE | | 15408 | 707K| | 8374 (5)| 00:00:01 | 25 | 25 | | | |
| 24 | SORT GROUP BY ROLLUP | | 15408 | 707K| | 8374 (5)| 00:00:01 | | | | | |
| 25 | TABLE ACCESS FULL | TABLE1 | 4040K| 181M| | 8081 (2)| 00:00:01 | 25 | 25 | | | |
| 26 | LOAD AS SELECT | SYS_TEMP_0FDA4B738_9AA210CA | | | | | | | | | | |
| 27 | PARTITION RANGE SINGLE | | 34 | 986 | | 8374 (5)| 00:00:01 | 25 | 25 | | | |
| 28 | SORT GROUP BY ROLLUP | | 34 | 986 | | 8374 (5)| 00:00:01 | | | | | |
| 29 | TABLE ACCESS FULL | TABLE1 | 4040K| 111M| | 8081 (2)| 00:00:01 | 25 | 25 | | | |
| 30 | LOAD AS SELECT | SYS_TEMP_0FDA4B738_9AA210CA | | | | | | | | | | |
| 31 | PARTITION RANGE SINGLE | | 6 | 168 | | 8374 (5)| 00:00:01 | 25 | 25 | | | |
| 32 | SORT GROUP BY ROLLUP | | 6 | 168 | | 8374 (5)| 00:00:01 | | | | | |
| 33 | TABLE ACCESS FULL | TABLE1 | 4040K| 107M| | 8081 (2)| 00:00:01 | 25 | 25 | | | |
| 34 | LOAD AS SELECT | SYS_TEMP_0FDA4B738_9AA210CA | | | | | | | | | | |
| 35 | PARTITION RANGE SINGLE | | 3 | 75 | | 8374 (5)| 00:00:01 | 25 | 25 | | | |
| 36 | SORT GROUP BY ROLLUP | | 3 | 75 | | 8374 (5)| 00:00:01 | | | | | |
| 37 | TABLE ACCESS FULL | TABLE1 | 4040K| 96M| | 8081 (2)| 00:00:01 | 25 | 25 | | | |
| 38 | PX COORDINATOR | | | | | | | | | | | |
| 39 | PX SEND QC (RANDOM) | :TQ20000 | 392K| 81M| | 97 (3)| 00:00:01 | | | Q2,00 | P->S | QC (RAND) |
| 40 | VIEW | | 392K| 81M| | 97 (3)| 00:00:01 | | | Q2,00 | PCWP | |
| 41 | PX BLOCK ITERATOR | | 392K| 20M| | 97 (3)| 00:00:01 | | | Q2,00 | PCWC | |
| 42 | TABLE ACCESS FULL | SYS_TEMP_0FDA4B738_9AA210CA | 392K| 20M| | 97 (3)| 00:00:01 | | | Q2,00 | PCWP | |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Note
-----
- Degree of Parallelism is 8 because of table property
53 rows selected.

Query statement consume quite long time on execution on oracle

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