Physical reads caused by WITH clause - sql

I have a set of complex optimized selects that suffer from physical reads. Without them they would be even faster!
These physical reads occur due to the WITH clause, one physical_read_request per WITH sub-query. They seem totally unnecessary to me, I'd prefer Oracle keeping the sub-query results in memory instead of writing them down to disk and reading them again.
I'm looking for a way how to get rid of these phys reads.
A simple sample having the same problems is this:
Edit: Example replaced with simpler one that does not use dictionary views.
alter session set STATISTICS_LEVEL=ALL;
create table T as
select level NUM from dual
connect by level <= 1000;
with /*a2*/ TT as (
select NUM from T
where NUM between 100 and 110
)
select * from TT
union all
select * from TT
;
SELECT * FROM TABLE(dbms_xplan.display_cursor(
(select sql_id from v$sql
where sql_fulltext like 'with /*a2*/ TT%'
and sql_fulltext not like '%v$sql%'
and sql_fulltext not like 'explain%'),
NULL, format=>'allstats last'));
and the corresponding execution plan is
SQL_ID bpqnhfdmxnqvp, child number 0
-------------------------------------
with /*a2*/ TT as ( select NUM from T where NUM between 100 and
110 ) select * from TT union all select * from TT
Plan hash value: 4255080040
---------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | Writes | OMem | 1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 22 |00:00:00.01 | 20 | 1 | 1 | | | |
| 1 | TEMP TABLE TRANSFORMATION | | 1 | | 22 |00:00:00.01 | 20 | 1 | 1 | | | |
| 2 | LOAD AS SELECT | | 1 | | 0 |00:00:00.01 | 8 | 0 | 1 | 266K| 266K| 266K (0)|
|* 3 | TABLE ACCESS FULL | T | 1 | 11 | 11 |00:00:00.01 | 4 | 0 | 0 | | | |
| 4 | UNION-ALL | | 1 | | 22 |00:00:00.01 | 9 | 1 | 0 | | | |
| 5 | VIEW | | 1 | 11 | 11 |00:00:00.01 | 6 | 1 | 0 | | | |
| 6 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6646_63A776 | 1 | 11 | 11 |00:00:00.01 | 6 | 1 | 0 | | | |
| 7 | VIEW | | 1 | 11 | 11 |00:00:00.01 | 3 | 0 | 0 | | | |
| 8 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6646_63A776 | 1 | 11 | 11 |00:00:00.01 | 3 | 0 | 0 | | | |
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("NUM">=100 AND "NUM"<=110))
Note
-----
- dynamic sampling used for this statement (level=2)
See the (phys) Write upon each WITH view creation, and the (phys) Read upon the first view usage. I also tried the RESULT_CACHE hint (which is not reflected in this sample select, but was reflected in my original queries), but it doesn't remove the disk accesses either (which is understandable).
How can I get rid of the phys writes/reads?

Related

Oracle Fetch Next Row Limit by unique id's

I have a table having data as shown below,
+-------+----------------+----------------+
| Id | HierUnitId | ObjectNumber |
+-------+----------------+----------------+
| 10 | 3599 | 1 |
| 10 | 3599 | 2 |
| 20 | 3599 | 3 |
| 20 | 3599 | 4 |
| 20 | 3599 | 1 |
| 30 | 3599 | 2 |
| 30 | 3599 | 3 |
+-------+----------------+----------------+
I have a select query
SELECT ID FROM TEST
FETCH NEXT :LIMIT ROWS ONLY
Now I want to limit the number of rows using the value of limit.
When the value of Limit is 2 I want two distinct id's i.e up to 5 rows. However, from query I will get only two rows having 10 as the id. Can someone help me in limiting the rows using distinct id?
What i want is total number of distinct id in the output is limit.
Use the DENSE_RANK analytic function to number the rows based on the unique/distinct ID values and then filter on that:
SELECT id
FROM (
SELECT ID,
DENSE_RANK() OVER (ORDER BY id) AS rnk
FROM test
)
WHERE rnk <= 2;
Which, for the sample data:
CREATE TABLE test (Id, HierUnitId, ObjectNumber ) AS
SELECT 10, 3599, 1 FROM DUAL UNION ALL
SELECT 10, 3599, 2 FROM DUAL UNION ALL
SELECT 20, 3599, 3 FROM DUAL UNION ALL
SELECT 20, 3599, 4 FROM DUAL UNION ALL
SELECT 20, 3599, 1 FROM DUAL UNION ALL
SELECT 30, 3599, 2 FROM DUAL UNION ALL
SELECT 30, 3599, 3 FROM DUAL;
Outputs:
ID
10
10
20
20
20
db<>fiddle here
As you said in the comment, you need to be able to define how many distinct ids should be shown. For that case i'd recommend you to find those ids first (see the distinct_ids part) and fetch all the lines you needed afterwards
with distinct_ids as (
select distinct id
from test_data
order by id
fetch first :limit rows only)
select id
from test_data td
join distinct_ids di
on td.id = di.id
If you need some distinct IDs without any particular order, then you may put fetch next ... into the subquery with distinct keyword. Index on ID column will be suitable to avoid two full table scans (I assume that ID cannot be null)
select /*+gather_plan_statistics*/
*
from t
where id in (
select distinct id
from t
where id is not null
fetch next 2 rows only
)
ID | HIERUNITID | OBJECTNUMBER
-: | ---------: | -----------:
1 | 3599 | 1
1 | 3599 | 2
2 | 3599 | 3
2 | 3599 | 4
2 | 3599 | 1
select *
from table(dbms_xplan.display_cursor(
format => 'ALL -PROJECTION -ALIAS ALLSTATS LAST'
))
| PLAN_TABLE_OUTPUT |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SQL_ID 2sqqq53kpy5rj, child number 0 |
| ------------------------------------- |
| select /*+gather_plan_statistics*/ * from t where id in ( select |
| distinct id from t where id is not null fetch next 2 rows only ) |
| |
| Plan hash value: 534568331 |
| |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| | Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| | 0 | SELECT STATEMENT | | 1 | | | 5 (100)| | 5 |00:00:00.01 | 3 | | | | |
| | 1 | MERGE JOIN SEMI | | 1 | 5 | 115 | 5 (40)| 00:00:01 | 5 |00:00:00.01 | 3 | | | | |
| | 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 7 | 70 | 2 (0)| 00:00:01 | 6 |00:00:00.01 | 2 | | | | |
| | 3 | INDEX FULL SCAN | T_IX | 1 | 7 | | 1 (0)| 00:00:01 | 6 |00:00:00.01 | 1 | | | | |
| |* 4 | SORT UNIQUE | | 6 | 2 | 26 | 3 (67)| 00:00:01 | 5 |00:00:00.01 | 1 | 2048 | 2048 | 2048 (0)| |
| | 5 | VIEW | VW_NSO_1 | 1 | 2 | 26 | 2 (50)| 00:00:01 | 2 |00:00:00.01 | 1 | | | | |
| |* 6 | VIEW | | 1 | 2 | 20 | 2 (50)| 00:00:01 | 2 |00:00:00.01 | 1 | | | | |
| |* 7 | WINDOW NOSORT STOPKEY | | 1 | 3 | 39 | 2 (50)| 00:00:01 | 2 |00:00:00.01 | 1 | 73728 | 73728 | | |
| | 8 | VIEW | | 1 | 3 | 39 | 2 (50)| 00:00:01 | 2 |00:00:00.01 | 1 | | | | |
| | 9 | SORT UNIQUE NOSORT | | 1 | 3 | 9 | 2 (50)| 00:00:01 | 2 |00:00:00.01 | 1 | | | | |
| |* 10 | INDEX FULL SCAN | T_IX | 1 | 7 | 21 | 1 (0)| 00:00:01 | 6 |00:00:00.01 | 1 | | | | |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| |
| Predicate Information (identified by operation id): |
| --------------------------------------------------- |
| |
| 4 - access("ID"="ID") |
| filter("ID"="ID") |
| 6 - filter("from$_subquery$_004"."rowlimit_$$_rownumber"<=2) |
| 7 - filter(ROW_NUMBER() OVER ( ORDER BY NULL)<=2) |
| 10 - filter("ID" IS NOT NULL) |
| |
db<>fiddle here

Execution plan too expensive case when exists

I have the below query, but when I execute it runs forever.
WITH aux AS (
SELECT
contract,
contract_account,
business_partner,
payment_plan,
installation,
contract_status
FROM
reta.mv_integrated_md a
WHERE
contract_status IN (
'LIVE',
'FINAL'
)
), aux1 AS (
SELECT
a.*,
CASE
WHEN EXISTS (
SELECT
NULL
FROM
aux b
WHERE
b.business_partner = a.business_partner
AND b.installation = a.installation
AND b.payment_plan = 'BMW'
) THEN
'X'
END h
FROM
aux a
)
SELECT
*
FROM
aux1;
My execution plan shows a huge cost which I cannot locate. How could I optimize this query? I have tried some hints but none of them have worked :(
Plan hash value: 1662974027
----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19M| 2000M| 825G (1)|999:59:59 | | |
|* 1 | VIEW | | 19M| 990M| 41331 (1)| 00:00:02 | | |
| 2 | TABLE ACCESS STORAGE FULL | SYS_TEMP_0FDA49C92_9A7BE8DE | 19M| 1066M| 41331 (1)| 00:00:02 | | |
| 3 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 4 | LOAD AS SELECT | SYS_TEMP_0FDA49C92_9A7BE8DE | | | | | | |
| 5 | PARTITION RANGE SINGLE | | 18M| 974M| 759K (1)| 00:00:30 | 1 | 1 |
|* 6 | TABLE ACCESS STORAGE FULL| MV_INTEGRATED_MD | 18M| 974M| 759K (1)| 00:00:30 | 1 | 1 |
| 7 | VIEW | | 19M| 2000M| 41331 (1)| 00:00:02 | | |
| 8 | TABLE ACCESS STORAGE FULL | SYS_TEMP_0FDA49C92_9A7BE8DE | 19M| 1066M| 41331 (1)| 00:00:02 | | |
----------------------------------------------------------------------------------------------------------------------------
Kindly let me know if any additional information needed.
Use window functions:
SELECT r.contract, r.contract_account, r.business_partner,
r.payment_plan, r.installation, r.contract_status,
MAX(CASE WHEN r.payment_plan = 'BMW' THEN 'X' END) OVER (PARTITION BY business_partner, installation) as h
FROM reta.mv_integrated_md#rbip r
WHERE r.contract_status IN ('LIVE', 'FINAL');
Not only is the query much simpler to write and read, but it should perform much better too.
Highest cost is due to FTS(Full table scan) on table/MV MV_INTEGRATED_MD.
Try to create index on contract_status and check if it reduces the cost and also, what is size of this mv/table in terms of block and it is 10 percent or more than total buffer cache size ?
TABLE ACCESS STORAGE FULL| MV_INTEGRATED_MD | 18M| 974M| 759K (1)| 00:00:30 | 1 | 1
If you run your query with the /*+ gather_plan_statistics */ hint (I'm simulating it with a 1000 row table) you imediately see the problem :
select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
-------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
-------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1000 |00:00:00.01 | 9 | 5 |
|* 1 | VIEW | | 1000 | 1000 | 1000 |00:00:00.09 | 0 | 0 |
| 2 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6737_1A17DE13 | 1000 | 1000 | 500K|00:00:00.08 | 0 | 0 |
| 3 | TEMP TABLE TRANSFORMATION | | 1 | | 1000 |00:00:00.01 | 9 | 5 |
| 4 | LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D6737_1A17DE13 | 1 | | 0 |00:00:00.01 | 8 | 5 |
|* 5 | TABLE ACCESS FULL | MV_INTEGRATED_MD | 1 | 1000 | 1000 |00:00:00.01 | 7 | 5 |
| 6 | VIEW | | 1 | 1000 | 1000 |00:00:00.01 | 0 | 0 |
| 7 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6737_1A17DE13 | 1 | 1000 | 1000 |00:00:00.01 | 0 | 0 |
-------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("B"."BUSINESS_PARTNER"=:B1 AND "B"."INSTALLATION"=:B2 AND "B"."PAYMENT_PLAN"='BMW'))
5 - filter("CONTRACT_STATUS"='LIVE')
It is in the line 2 where a full scan is activated in a loop for each line of the main table (see starts = 1000)
Typically you want to resolve the EXISTS with a semi join to preserve good performance, but here it seems that Oracle can not rewrite it.
So you'll need to rewrite the query yourself.
Despite the excelent proposal of #GordonLinoff (that I'll start with) you may try to use an outer join as follows
with bmw as (
select distinct business_partner, installation
from mv_integrated_md
where payment_plan = 'BMW')
SELECT
a.contract,
a.contract_account,
a.business_partner,
a.payment_plan,
a.installation,
a.contract_status,
case when b.business_partner is not null then 'X' end as h
FROM mv_integrated_md a
left outer join bmw b
on b.business_partner = a.business_partner and
b.installation = a.installation
WHERE a.contract_status IN ( 'LIVE', 'FINAL')
This will lead to two fulls scans, one deduplication and outer join.

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"='*'))

Performance Tuning Oracle SQL involving huge tables

I have an Oracle SQL query that involves joins with 4 large tables along with few small tables.
Large tables are TBL_1, TBL_2, TBL_3, TBL4 each having approx 8M records. Rest are small tables having less than 10K records.
The problem: Query takes more than 3 minutes even if there is no data to return.
The table and index stats are up-to-date. There are no stale stats on these tables.
I've tried using hints but it didn't work.
Please see my observations below:
Query:
SELECT a.*, ROWNUM AS rnm
FROM ( SELECT c.idntfr,
pr.program_name AS "Program",
e.case_number,
(SELECT DECODE (s.status_name,
'EA', 'A',
'ED', 'D',
'EU', 'U',
s.status_name)
FROM TBL_5 ms, status s
WHERE ms.status_type_cid = 7
AND mbr_sid = c.mbr_sid
AND ms.status_type_cid = s.status_type_cid
AND s.status_cid = ms.status_cid
AND ms.oprtnl_flag = 'A'
AND SYSDATE BETWEEN ms.from_date AND ms.TO_DATE),
DECODE (
LENGTH (TRIM (e.social_security_nmbr)),
NULL, 'Not Available',
( SUBSTR (e.social_security_nmbr, 1, 3)
|| '-'
|| SUBSTR (e.social_security_nmbr, 4, 2)
|| '-'
|| SUBSTR (e.social_security_nmbr, 6, 4)))
AS "SSN",
e.last_name || ',' || e.first_name || ' ' || e.middle_name,
TO_CHAR (e.injury_date, 'MM/dd/yyyy'),
DECODE (e.gender_lkpcd,
'M', 'Male',
'F', 'Female',
'U', 'Unknown'),
e.mbr_sid,
pr.program_cid,
e.last_name,
e.social_security_nmbr,
e.first_name AS
FROM TBL_1 c,
program pr,
TBL_2 e,
TBL_3 mai,
TBL_4 uaxou
WHERE c.mbr_sid = e.mbr_sid
AND c.mbr_sid = mai.mbr_sid
AND c.oprtnl_flag = 'A'
AND c.idntfr_type_cid = 423
AND TRUNC (SYSDATE) BETWEEN c.from_date AND c.TO_DATE
AND TRUNC (SYSDATE) BETWEEN e.from_date AND e.TO_DATE
AND e.oprtnl_flag = 'A'
AND e.status_cid = 2
AND mai.oprtnl_flag = 'A'
AND mai.status_cid = 2
AND TRUNC (SYSDATE) BETWEEN mai.from_date AND mai.TO_DATE
AND e.program_code = pr.program_code
AND pr.oprtnl_flag = 'A'
AND uaxou.user_acct_sid = 1
AND uaxou.oprtnl_flag = 'A'
AND SYSDATE BETWEEN uaxou.from_date AND uaxou.TO_DATE
AND uaxou.org_unit_sid = mai.org_unit_sid
ORDER BY "Program" ASC) a
WHERE ROWNUM < 102;
There is no data for the following condition
AND uaxou.user_acct_sid = 1
Expected Result: Response time should be under 4 seconds if NO data is returned.
Explain Plan:
Plan hash value: 2272581586
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 101 | 22220 | 1361 (1)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 58 | 7 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 58 | 7 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID BATCHED| TBL_5 | 1 | 31 | 6 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | XIF1TBL_5 | 8 | | 3 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | XPKSTATUS | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | STATUS | 1 | 27 | 1 (0)| 00:00:01 |
|* 7 | COUNT STOPKEY | | | | | |
| 8 | VIEW | | 169 | 37180 | 1361 (1)| 00:00:01 |
| 9 | NESTED LOOPS | | 169 | 36166 | 767 (0)| 00:00:01 |
| 10 | NESTED LOOPS | | 11904 | 36166 | 767 (0)| 00:00:01 |
| 11 | NESTED LOOPS | | 62 | 11284 | 333 (0)| 00:00:01 |
| 12 | NESTED LOOPS | | 45 | 6660 | 108 (0)| 00:00:01 |
| 13 | NESTED LOOPS | | 33 | 3564 | 9 (0)| 00:00:01 |
|* 14 | TABLE ACCESS BY INDEX ROWID | PROGRAM | 5 | 70 | 2 (0)| 00:00:01 |
| 15 | INDEX FULL SCAN | XAK1OWCP_PROGRAM | 2 | | 1 (0)| 00:00:01 |
|* 16 | TABLE ACCESS FULL | TBL_2 | 20 | 1880 | 4 (0)| 00:00:01 |
|* 17 | TABLE ACCESS BY INDEX ROWID | TBL_1 | 1 | 40 | 3 (0)| 00:00:01 |
|* 18 | INDEX RANGE SCAN | TUNE_WS_19NOV10_X2 | 1 | | 2 (0)| 00:00:01 |
|* 19 | TABLE ACCESS BY INDEX ROWID | TBL_3 | 1 | 34 | 5 (0)| 00:00:01 |
|* 20 | INDEX RANGE SCAN | XIE2_TBL_3 | 3 | | 2 (0)| 00:00:01 |
|* 21 | INDEX RANGE SCAN | XIF3TBL_4 | 192 | | 1 (0)| 00:00:01 |
|* 22 | TABLE ACCESS BY INDEX ROWID | TBL_4 | 3 | 96 | 7 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("MS"."STATUS_TYPE_CID"=7 AND "MS"."OPRTNL_FLAG"='A' AND "MS"."TO_DATE">=SYSDATE#! AND
"MS"."FROM_DATE"<=SYSDATE#!)
4 - access("MBR_SID"=:B1)
5 - access("S"."STATUS_TYPE_CID"=7 AND "S"."STATUS_CID"="MS"."STATUS_CID")
7 - filter(ROWNUM<102)
14 - filter("PR"."OPRTNL_FLAG"='A')
16 - filter("E"."PROGRAM_CODE"="PR"."PROGRAM_CODE" AND "E"."OPRTNL_FLAG"='A' AND "E"."STATUS_CID"=2 AND
"E"."FROM_DATE"<=TRUNC(SYSDATE#!) AND TRUNC(INTERNAL_FUNCTION("FROM_DATE"))<=TRUNC(TRUNC(SYSDATE#!)) AND
"E"."TO_DATE">=TRUNC(SYSDATE#!) AND TRUNC(INTERNAL_FUNCTION("TO_DATE"))>=TRUNC(TRUNC(SYSDATE#!)))
17 - filter("C"."FROM_DATE"<=TRUNC(SYSDATE#!) AND "C"."TO_DATE">=TRUNC(SYSDATE#!))
18 - access("C"."MBR_SID"="E"."MBR_SID" AND "C"."IDNTFR_TYPE_CID"=423 AND "C"."OPRTNL_FLAG"='A')
19 - filter("MAI"."OPRTNL_FLAG"='A' AND "MAI"."STATUS_CID"=2 AND "MAI"."FROM_DATE"<=TRUNC(SYSDATE#!) AND
"MAI"."TO_DATE">=TRUNC(SYSDATE#!))
20 - access("C"."MBR_SID"="MAI"."MBR_SID")
21 - access("UAXOU"."USER_ACCT_SID"=1)
22 - filter("UAXOU"."ORG_UNIT_SID"="MAI"."ORG_UNIT_SID" AND "UAXOU"."OPRTNL_FLAG"='A' AND
"UAXOU"."FROM_DATE"<=SYSDATE#! AND "UAXOU"."TO_DATE">=SYSDATE#!)
This is output of query from v$parameter
NAME | VALUE
compatible | 12.2.0
optimizer_adaptive_plans | TRUE
optimizer_adaptive_reporting_only | FALSE
optimizer_features_enable | 12.2.0.1
This is the explain plan after adding GATHER_PLAN_STATISTICS showing the actual cardinality values:
Plan hash value: 2272581586
-------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 0 |00:00:00.01 | 0 |
| 1 | NESTED LOOPS | | 0 | 1 | 0 |00:00:00.01 | 0 |
| 2 | NESTED LOOPS | | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 3 | TABLE ACCESS BY INDEX ROWID BATCHED| TBL_5 | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 4 | INDEX RANGE SCAN | XIF1TBL_5 | 0 | 8 | 0 |00:00:00.01 | 0 |
|* 5 | INDEX UNIQUE SCAN | XPKSTATUS | 0 | 1 | 0 |00:00:00.01 | 0 |
| 6 | TABLE ACCESS BY INDEX ROWID | STATUS | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 7 | COUNT STOPKEY | | 1 | | 0 |00:00:00.01 | 0 |
| 8 | VIEW | | 1 | 169 | 0 |00:00:00.01 | 0 |
| 9 | NESTED LOOPS | | 1 | 169 | 0 |00:00:00.01 | 0 |
| 10 | NESTED LOOPS | | 1 | 11904 | 0 |00:00:00.01 | 0 |
| 11 | NESTED LOOPS | | 1 | 62 | 0 |00:00:00.01 | 0 |
| 12 | NESTED LOOPS | | 1 | 45 | 0 |00:00:00.01 | 0 |
| 13 | NESTED LOOPS | | 1 | 33 | 0 |00:00:00.01 | 0 |
|* 14 | TABLE ACCESS BY INDEX ROWID | PROGRAM | 1 | 5 | 1 |00:00:00.01 | 2 |
| 15 | INDEX FULL SCAN | XAK1OWCP_PROGRAM | 1 | 2 | 2 |00:00:00.01 | 1 |
|* 16 | TABLE ACCESS FULL | TBL_2 | 1 | 20 | 0 |00:00:00.01 | 0 |
|* 17 | TABLE ACCESS BY INDEX ROWID | TBL_1 | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 18 | INDEX RANGE SCAN | TUNE_WS_19NOV10_X2 | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 19 | TABLE ACCESS BY INDEX ROWID | TBL_3 | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 20 | INDEX RANGE SCAN | XIE2_TBL_3 | 0 | 3 | 0 |00:00:00.01 | 0 |
|* 21 | INDEX RANGE SCAN | XIF3TBL_4 | 0 | 192 | 0 |00:00:00.01 | 0 |
|* 22 | TABLE ACCESS BY INDEX ROWID | TBL_4 | 0 | 3 | 0 |00:00:00.01 | 0 |
-------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("MS"."STATUS_TYPE_CID"=7 AND "MS"."OPRTNL_FLAG"='A' AND "MS"."TO_DATE">=SYSDATE#! AND
"MS"."FROM_DATE"<=SYSDATE#!))
4 - access("MBR_SID"=:B1)
5 - access("S"."STATUS_TYPE_CID"=7 AND "S"."STATUS_CID"="MS"."STATUS_CID")
7 - filter(ROWNUM<102)
14 - filter("PR"."OPRTNL_FLAG"='A')
16 - filter(("E"."PROGRAM_CODE"="PR"."PROGRAM_CODE" AND "E"."OPRTNL_FLAG"='A' AND "E"."STATUS_CID"=2 AND
"E"."FROM_DATE"<=TRUNC(SYSDATE#!) AND "E"."TO_DATE">=TRUNC(SYSDATE#!)))
17 - filter(("C"."FROM_DATE"<=TRUNC(SYSDATE#!) AND "C"."TO_DATE">=TRUNC(SYSDATE#!)))
18 - access("C"."MBR_SID"="E"."MBR_SID" AND "C"."IDNTFR_TYPE_CID"=423 AND "C"."OPRTNL_FLAG"='A')
19 - filter(("MAI"."OPRTNL_FLAG"='A' AND "MAI"."STATUS_CID"=2 AND "MAI"."FROM_DATE"<=TRUNC(SYSDATE#!) AND
"MAI"."TO_DATE">=TRUNC(SYSDATE#!)))
20 - access("C"."MBR_SID"="MAI"."MBR_SID")
21 - access("UAXOU"."USER_ACCT_SID"=1)
22 - filter(("UAXOU"."ORG_UNIT_SID"="MAI"."ORG_UNIT_SID" AND "UAXOU"."OPRTNL_FLAG"='A' AND
"UAXOU"."FROM_DATE"<=SYSDATE#! AND "UAXOU"."TO_DATE">=SYSDATE#!))
I've tried various hints USE_HASH(c e) and various other combinations, none worked.
One interesting observation, if I comment the condition:
--AND uaxou.user_acct_sid = 1
The results came in 7 seconds. (Obviously, data is returned in this case).
So, what's causing the query to take so long when no data is returned? (i.e. this condition is not commented AND uaxou.user_acct_sid = 1)
I let the slow query to finish, it took 10 min 46 seconds. No data is returned
Here is the explain plan. I don't know why A-Time doesn't match with actual execution time.
Plan hash value: 2272581586
-------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 0 |00:00:00.01 | 0 |
| 1 | NESTED LOOPS | | 0 | 1 | 0 |00:00:00.01 | 0 |
| 2 | NESTED LOOPS | | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 3 | TABLE ACCESS BY INDEX ROWID BATCHED| TBL_5 | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 4 | INDEX RANGE SCAN | XIF1TBL_5 | 0 | 8 | 0 |00:00:00.01 | 0 |
|* 5 | INDEX UNIQUE SCAN | XPKSTATUS | 0 | 1 | 0 |00:00:00.01 | 0 |
| 6 | TABLE ACCESS BY INDEX ROWID | STATUS | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 7 | COUNT STOPKEY | | 1 | | 0 |00:00:00.01 | 0 |
| 8 | VIEW | | 1 | 169 | 0 |00:00:00.01 | 0 |
| 9 | NESTED LOOPS | | 1 | 169 | 0 |00:00:00.01 | 0 |
| 10 | NESTED LOOPS | | 1 | 11904 | 0 |00:00:00.01 | 0 |
| 11 | NESTED LOOPS | | 1 | 62 | 0 |00:00:00.01 | 0 |
| 12 | NESTED LOOPS | | 1 | 45 | 0 |00:00:00.01 | 0 |
| 13 | NESTED LOOPS | | 1 | 33 | 0 |00:00:00.01 | 0 |
|* 14 | TABLE ACCESS BY INDEX ROWID | PROGRAM | 1 | 5 | 1 |00:00:00.01 | 2 |
| 15 | INDEX FULL SCAN | XAK1OWCP_PROGRAM | 1 | 2 | 2 |00:00:00.01 | 1 |
|* 16 | TABLE ACCESS FULL | TBL_2 | 1 | 20 | 0 |00:00:00.01 | 0 |
|* 17 | TABLE ACCESS BY INDEX ROWID | TBL_1 | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 18 | INDEX RANGE SCAN | TUNE_WS_19NOV10_X2 | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 19 | TABLE ACCESS BY INDEX ROWID | TBL_3 | 0 | 1 | 0 |00:00:00.01 | 0 |
|* 20 | INDEX RANGE SCAN | XIE2_TBL_3 | 0 | 3 | 0 |00:00:00.01 | 0 |
|* 21 | INDEX RANGE SCAN | XIF3TBL_4 | 0 | 192 | 0 |00:00:00.01 | 0 |
|* 22 | TABLE ACCESS BY INDEX ROWID | TBL_4 | 0 | 3 | 0 |00:00:00.01 | 0 |
-------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("MS"."STATUS_TYPE_CID"=7 AND "MS"."OPRTNL_FLAG"='A' AND "MS"."TO_DATE">=SYSDATE#! AND
"MS"."FROM_DATE"<=SYSDATE#!))
4 - access("MBR_SID"=:B1)
5 - access("S"."STATUS_TYPE_CID"=7 AND "S"."STATUS_CID"="MS"."STATUS_CID")
7 - filter(ROWNUM<102)
14 - filter("PR"."OPRTNL_FLAG"='A')
16 - filter(("E"."PROGRAM_CODE"="PR"."PROGRAM_CODE" AND "E"."OPRTNL_FLAG"='A' AND "E"."STATUS_CID"=2 AND
"E"."FROM_DATE"<=TRUNC(SYSDATE#!) AND "E"."TO_DATE">=TRUNC(SYSDATE#!)))
17 - filter(("C"."FROM_DATE"<=TRUNC(SYSDATE#!) AND "C"."TO_DATE">=TRUNC(SYSDATE#!)))
18 - access("C"."MBR_SID"="E"."MBR_SID" AND "C"."IDNTFR_TYPE_CID"=423 AND "C"."OPRTNL_FLAG"='A')
19 - filter(("MAI"."OPRTNL_FLAG"='A' AND "MAI"."STATUS_CID"=2 AND "MAI"."FROM_DATE"<=TRUNC(SYSDATE#!) AND
"MAI"."TO_DATE">=TRUNC(SYSDATE#!)))
20 - access("C"."MBR_SID"="MAI"."MBR_SID")
21 - access("UAXOU"."USER_ACCT_SID"=1)
22 - filter(("UAXOU"."ORG_UNIT_SID"="MAI"."ORG_UNIT_SID" AND "UAXOU"."OPRTNL_FLAG"='A' AND
"UAXOU"."FROM_DATE"<=SYSDATE#! AND "UAXOU"."TO_DATE">=SYSDATE#!))
Adaptive plans may improve the execution plan.
You tagged the question as 12c but it looks like the execution plan is not using adaptive plans for some reason. Adaptive plans let Oracle change operations at run time, such as switching between NESTED LOOPS and HASH JOINS.
NESTED LOOPS are good for a small percentage of rows, and HASH JOINS are good for a large percentage of rows. Since the ROWS estimates are all small, but the query runs for three minutes, I would guess the optimizer is significantly underestimating the cardinality of the expressions and joins, and is using too many NESTED LOOPs.
If adaptive plans were enabled the execution plan should have this at the bottom:
Note
-----
- this is an adaptive plan
Since that Note is missing, I would guess there is a parameter issue on your database preventing adaptive plans. Run the below query and see if one of the features is turned off, or if the features are set to a version lower than 12:
select name, value
from v$parameter
where name in (
'optimizer_adaptive_features', --12.1 only
'optimizer_adaptive_plans', --12.2+
'optimizer_adaptive_reporting_only',
'optimizer_features_enable',
'compatible'
)
order by 1;
EDIT 1
I'm not sure why adaptive plans aren't working for you. If nobody can figure that out, then we'll need to investigate the execution plan actual values to find out precisely which operations are slow.
There are at least two ways to get the actual numbers. If you can change and re-run the offending query, you can use the hint GATHER_PLAN_STATISTICS.
--Run slow query and wait for it to finish:
select /*+ gather_plan_statistics */ * from dual;
--Find the SQL_ID of the query using some distinctive text:
select *
from v$sql
where lower(sql_fulltext) like '%gather_plan_statistics%';
--Generate execution plan with actual values.
select *
from table(dbms_xplan.display_cursor(sql_id => 'SQL_ID from above', format=>'allstats last'));
If you cannot alter the query, you can use SQL Monitor reports to find the actual values. (This feature requires Enterprise Edition and a tuning pack license.)
--Generate SQL Monitoring Report:
select dbms_sqltune.report_sql_monitor(sql_id => 'SQL_ID from above') from dual;
EDIT 2
Are you 100% sure you've found the right SQL_ID? You might want to double check GV$SQL. Sometimes the case is switched, if the SQL is submitted from an application or PL/SQL block. And rarely the real SQL statement will age out of GV$SQL, if someone runs alter system flush shared_pool;, or stats are gathered, or if you wait too long.
If that is truly the right execution plan, no time is spent on the query. Normally this means that time must be spent by the network sending the results, or the application processing the results. But since there are no rows returned a network or application issue doesn't sound likely.
If the time is spent on the database, but not on that query, then I would guess it's either a parsing issue or a recursive query issue. Parsing issues can be found by tracing, but those are caused by extremely unusual problems or queries, and likely isn't the case here.
Perhaps one of the queries Oracle uses to gather metadata is taking too long. For many queries, Oracle needs to run other queries that check privileges, dynamic sampling, etc. You may need to tune one of those other queries, and the below statements can help with that painful process:
--Clear existing run times (be careful running this on production).
--(This won't flush queries that are actively running.)
alter system flush shared_pool;
--Run your slow SQL statement here.
--...
--Now look for anything "weird" that has taken up most of the time.
select elapsed_time/1000000 seconds, gv$sql.*
from gv$sql
order by seconds desc;

ORACLE SQL,PL/SQL limitations [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
In Oracle we have the limitation of only 1000 items supported in the IN clause of SQL. I would like to know if there are any other such limitations in Oracle.
Various limits are documented here for 11.2: http://docs.oracle.com/cd/B28359_01/server.111/b28320/limits003.htm
Some edition-based limitations: http://www.oracle.com/us/products/database/enterprise-edition/comparisons/index.html
Limits for Oracle 10g:
http://docs.oracle.com/cd/B19306_01/server.102/b14237/limits.htm
Logical limits:
http://docs.oracle.com/cd/B19306_01/server.102/b14237/limits003.htm
Physical limits:
http://docs.oracle.com/cd/B19306_01/server.102/b14237/limits002.htm
There is a simple trick to bypass this limit.
I use it sometimes in ad hoc queries.
Tested on Oracle 11.2g for 5000 items.
Drawback of this method is long parse time (about 5-10 seconds on my system for 5000 items).
WITH list AS (
select 1 as X from dual union all
select 2 from dual union all
select 3 from dual union all
......
......
......
select 4997 from dual union all
select 4998 from dual union all
select 4999 from dual union all
select 5000 from dual
)
SELECT /*+gather_plan_statistics */ * FROM table123
WHERE x IN ( SELECT * FROM list );
select * from table( dbms_xplan.display_cursor (format=>'ALLSTATS LAST'));
----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 5000 |00:00:00.24 | 29 | | | |
|* 1 | HASH JOIN RIGHT SEMI| | 1 | 1 | 5000 |00:00:00.24 | 29 | 1066K| 1066K| 1339K (0)|
| 2 | VIEW | VW_NSO_1 | 1 | 5000 | 5000 |00:00:00.24 | 0 | | | |
| 3 | VIEW | | 1 | 5000 | 5000 |00:00:00.21 | 0 | | | |
| 4 | UNION-ALL | | 1 | | 5000 |00:00:00.18 | 0 | | | |
| 5 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
| 6 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
| 7 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
| 8 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
| 9 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
..........
..........
..........
|5000 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
|5001 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
|5002 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
|5003 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
|5004 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | | | |
|5005 | TABLE ACCESS FULL | TABLE123 | 1 | 9999 | 9999 |00:00:00.02 | 29 | | | |
----------------------------------------------------------------------------------------------------------------------