How to optimize an Oracle UNION ALL of two 3-sec queries that takes over 200 secs ... even with rownum - oracle

I am working to further optimize the following query. I have already had a lot of success optimizing the query up to this point, but now I am looking for help from others for ideas. I appreciate your help.
The following query takes over 200 seconds to complete yet the two individual WITH clause queries (receiving_1 and receiving_2) that make up the "UNION ALL" take less than 4 seconds each.
receiving_1 -- 58 recs in 3.4 secs; cost 295 (no UNION)
receiving_2 -- 0 recs in 3.8 secs; cost 295 (no UNION)
UNION ALL of 1+2 -- 58 recs in 203.1 secs; cost 300 (UNION ALL)
I was hoping "rownum" would help optimize the "UNION ALL", but it did not. This attempt was based on a note from AskTom --> Files --> SQLTechniques.zip (between 61 and 72)
When you have two queries that run light speed separately
But not so together
Generally a mixed “CBO/RBO” problem
Use of RBO with a feature that kicks in the CBO
Rownum can be a temporary fix till all things are CBO
NOTE: One optimization that avoids this issue is embedded replacement parameters or cursor parameters that greatly reduce the rows at key points in the query. However, because I need to run this query through a database link against a read-only database, the following options are not available for this query.
dbms_sql over database link: query is too long; dbs_sql does not accept a lob over database link; also DBAs don't allow the running of any PL/SQL in the read-only database.
pipelined function: pipelined function has known bug over database link between 19c and 11g so fails
sys_refcursor via PL/SQL: cursors are not supported over database links
set parameter values in PL/SQL package and use them in view: not allowed to execute any code in read-only database (I am still bugging the DBAs to allow this. This might be my best hope if I am not able to optimize the below query to run as a view.)
NOTE: In an attempt at providing a good test case with as little code as possible, the below SQL (4,584 chars, 88 line breaks) is a greatly simplified version of the actual SQL (93,683 chars, 2,014 line breaks) I am developing. The original SQL has already gone through several rounds of optimizations to bring the runtime down from over an hour to under 10 seconds. This latest challenge is that the SQL has to be rewritten as a view as opposed to a SQL file with embedded substitution parameters. The substitution parameters were key to the optimizations up to now. I am now refactoring the SQL to work as a view in Oracle 11gR2 to be called via a database link from Oracle 19c. A view has no parameters, obviously. The driving parameters (ledger, OU, period) will be WHERE clause conditions when querying the view.
Big picture: The actual SQL under development is an amalgamation of 6 report queries that share a lot of duplicated code. My work is to convert all six source report queries into an efficient extract for importing into a new database schema for reconciliation purposes.
with
gccs as (
select
gcc.code_combination_id
from apps.gl_code_combinations gcc
where 1=1
AND regexp_like(gcc.segment1,'^[0-9]{4}$') -- only include 4-digit numbers
AND gcc.segment1 BETWEEN to_char('1500') and to_char('1504')
AND regexp_like(gcc.segment3,'^[0-9]{4}$') -- only include 4-digit numbers
AND ( gcc.segment3 BETWEEN to_char('5000') and to_char('5999')
OR gcc.segment3 in ('6000','6002','6003','6004','6005','6006','6007','6008','6009','6010','6011','6012','6013','6014','6017','6018','6019','6020','6021','6022','6023','6024','6025','6026','6027','6028','6029','6030','6031','6032',
'6033','6034','6035','6036','6037','6038','6039','6040','6041','6042','6043','6044','6045','6046','6047','6048','6049','6050','6051','6052','6053','6054','6055','6056','6058','6060','6061','6062','6063','6064',
'6065','6066','6067','6068','6069','6070','6071','6072','6073','6074','6075','6076','6077','6084','6085','6086','6087','6088','6089','6090','6091','6092','6093','6094','6095','6096','6097','6098','6099','6100',
'6101','6102','6103','6104','6105','6106','6120','6121','6130','6131','6140','6150','6151','6155','6158','6162','6990','6991','6992','6993','6994','6995','6996','6997','6998'))
AND regexp_like(gcc.segment6,'^[0-9]{4}$') -- only include 4-digit numbers
AND gcc.segment6 BETWEEN to_char('3000') and to_char('4999')
) -- select count(*) code_combination_id from gccs;
,
base_query as (
SELECT
gl.LEDGER_ID,
hou.organization_id,
gper.PERIOD_NAME,
XAL.AE_HEADER_ID,
XAL.AE_LINE_NUM,
GJB.STATUS BATCH_STATUS,
GJH.JE_CATEGORY,
XAL.ACCOUNTING_CLASS_CODE,
GJH.JE_SOURCE,
GJH.JE_HEADER_ID,
GJL.JE_LINE_NUM
FROM apps.gl_ledgers gl
join apps.hr_operating_units hou
on hou.set_of_books_id = gl.ledger_id
join APPS.GL_JE_LINES GJL
on exists (select null from gccs where gccs.CODE_COMBINATION_ID = GJL.CODE_COMBINATION_ID)
AND (nvl(gjl.stat_amount,0) <> 0 OR (NVL (gjl.accounted_dr, 0) - NVL (gjl.accounted_cr, 0)) <> 0)
join APPS.GL_PERIODS gper
on GJL.PERIOD_NAME = gper.PERIOD_NAME
join APPS.GL_JE_HEADERS GJH
on GJH.JE_HEADER_ID = GJL.JE_HEADER_ID
AND GJH.PERIOD_NAME = gper.PERIOD_NAME
AND GJH.LEDGER_ID = gl.ledger_id
AND GJH.JE_SOURCE = 'Cost Management'
join APPS.GL_JE_BATCHES GJB
on GJB.JE_BATCH_ID = GJH.JE_BATCH_ID
join APPS.GL_IMPORT_REFERENCES GIR
on GIR.JE_HEADER_ID = GJL.JE_HEADER_ID
AND GIR.JE_LINE_NUM = GJL.JE_LINE_NUM
join APPS.XLA_AE_LINES XAL
on XAL.GL_SL_LINK_TABLE = GIR.GL_SL_LINK_TABLE
AND XAL.GL_SL_LINK_ID = GIR.GL_SL_LINK_ID
AND XAL.LEDGER_ID = gl.LEDGER_ID
),
receiving_1 as (
select distinct
bq.LEDGER_ID,
bq.organization_id,
bq.period_name,
bq.AE_HEADER_ID,
bq.AE_LINE_NUM
FROM base_query bq
WHERE bq.BATCH_STATUS = 'P'
AND UPPER(bq.JE_CATEGORY) = UPPER('Receiving')
),
receiving_2 as (
select distinct
bq.LEDGER_ID,
bq.organization_id,
bq.period_name,
bq.AE_HEADER_ID,
bq.AE_LINE_NUM
FROM base_query bq
WHERE bq.ACCOUNTING_CLASS_CODE = 'INTRA'
AND bq.BATCH_STATUS = 'P'
AND UPPER(bq.JE_CATEGORY) = UPPER('Receiving')
),
receiving_all as (
-- the use of "rownum" below sometimes helps to optimize "union all", but does not help in this query
select rownum, x.* from receiving_1 x -- 58 recs in 3.4 secs cost 295 (no UNION)
union all -- 58 recs in 203.1 secs cost 300 (UNION ALL)
select rownum, x.* from receiving_2 x -- 0 recs in 3.8 secs cost 295 (no UNION)
)
select count(*)
from receiving_all
where ledger_id = 2021
and organization_id = 82
and period_name = 'SEP-2020';
AUTOTRACE for receiving_1 part of query"
COUNT(*)
----------
58
Execution Plan
----------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 35 | 295 (1)| | |
| 1 | SORT AGGREGATE | | 1 | 35 | | | |
| 2 | VIEW | | 1 | 35 | 295 (1)| | |
| 3 | COUNT | | | | | | |
| 4 | VIEW | | 1 | 35 | 295 (1)| | |
| 5 | HASH UNIQUE | | 1 | 266 | 295 (1)| | |
| 6 | NESTED LOOPS | | 1 | 266 | 294 (1)| | |
| 7 | NESTED LOOPS | | 1 | 266 | 294 (1)| | |
| 8 | NESTED LOOPS | | 1 | 237 | 236 (1)| | |
| 9 | NESTED LOOPS | | 1 | 214 | 178 (1)| | |
| 10 | NESTED LOOPS | | 1 | 207 | 176 (1)| | |
| 11 | NESTED LOOPS | | 1 | 183 | 176 (1)| | |
| 12 | NESTED LOOPS | | 1 | 152 | 173 (1)| | |
| 13 | NESTED LOOPS | | 1 | 148 | 173 (1)| | |
| 14 | NESTED LOOPS | | 1 | 123 | 172 (1)| | |
| 15 | NESTED LOOPS | | 1 | 101 | 169 (1)| | |
| 16 | NESTED LOOPS | | 1 | 97 | 169 (1)| | |
| 17 | NESTED LOOPS | | 1 | 53 | 167 (1)| | |
| 18 | SORT UNIQUE | | 1 | 21 | 22 (0)| | |
| 19 | TABLE ACCESS BY INDEX ROWID| GL_CODE_COMBINATIONS | 1 | 21 | 22 (0)| | |
| 20 | INDEX RANGE SCAN | GL_CODE_COMBINATIONS_N1 | 117 | | 8 (0)| | |
| 21 | TABLE ACCESS BY INDEX ROWID | GL_JE_LINES | 16 | 512 | 144 (0)| | |
| 22 | INDEX RANGE SCAN | GL_JE_LINES_N1 | 319 | | 4 (0)| | |
| 23 | TABLE ACCESS BY INDEX ROWID | GL_JE_HEADERS | 1 | 44 | 2 (0)| | |
| 24 | INDEX UNIQUE SCAN | GL_JE_HEADERS_U1 | 1 | | 1 (0)| | |
| 25 | INDEX UNIQUE SCAN | GL_LEDGERS_U2 | 1 | 4 | 0 (0)| | |
| 26 | TABLE ACCESS BY INDEX ROWID | HR_ORGANIZATION_INFORMATION | 1 | 22 | 3 (0)| | |
| 27 | INDEX RANGE SCAN | HR_ORGANIZATION_INFORMATIO_FK1 | 30 | | 1 (0)| | |
| 28 | INDEX RANGE SCAN | GL_PERIODS_U1 | 1 | 25 | 1 (0)| | |
| 29 | INDEX UNIQUE SCAN | HR_ORGANIZATION_UNITS_PK | 1 | 4 | 0 (0)| | |
| 30 | TABLE ACCESS BY INDEX ROWID | HR_ORGANIZATION_INFORMATION | 1 | 31 | 3 (0)| | |
| 31 | INDEX RANGE SCAN | HR_ORGANIZATION_INFORMATIO_FK2 | 2 | | 1 (0)| | |
| 32 | INDEX UNIQUE SCAN | HR_ALL_ORGANIZATION_UNTS_TL_PK | 1 | 24 | 0 (0)| | |
| 33 | TABLE ACCESS BY INDEX ROWID | GL_JE_BATCHES | 1 | 7 | 2 (0)| | |
| 34 | INDEX UNIQUE SCAN | GL_JE_BATCHES_U1 | 1 | | 1 (0)| | |
| 35 | TABLE ACCESS BY INDEX ROWID | GL_IMPORT_REFERENCES | 105 | 2415 | 58 (0)| | |
| 36 | INDEX RANGE SCAN | GL_IMPORT_REFERENCES_N1 | 105 | | 3 (0)| | |
| 37 | PARTITION LIST ALL | | 1 | | 57 (0)| 1 | 19 |
| 38 | INDEX RANGE SCAN | XLA_AE_LINES_N4 | 1 | | 57 (0)| 1 | 19 |
| 39 | TABLE ACCESS BY LOCAL INDEX ROWID | XLA_AE_LINES | 1 | 29 | 58 (0)| 1 | 1 |
-------------------------------------------------------------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
Statistics
----------------------------------------------------------
99 recursive calls
0 db block gets
3500320 consistent gets
0 physical reads
116 redo size
346 bytes sent via SQL*Net to client
4388 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed
AUTOTRACE for receiving_2 part of query:
COUNT(*)
----------
0
Execution Plan
----------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 35 | 295 (1)| | |
| 1 | SORT AGGREGATE | | 1 | 35 | | | |
| 2 | VIEW | | 1 | 35 | 295 (1)| | |
| 3 | COUNT | | | | | | |
| 4 | VIEW | | 1 | 35 | 295 (1)| | |
| 5 | HASH UNIQUE | | 1 | 279 | 295 (1)| | |
| 6 | NESTED LOOPS | | 1 | 279 | 294 (1)| | |
| 7 | NESTED LOOPS | | 1 | 279 | 294 (1)| | |
| 8 | NESTED LOOPS | | 1 | 272 | 292 (1)| | |
| 9 | NESTED LOOPS | | 1 | 230 | 234 (1)| | |
| 10 | NESTED LOOPS | | 1 | 207 | 176 (1)| | |
| 11 | NESTED LOOPS | | 1 | 183 | 176 (1)| | |
| 12 | NESTED LOOPS | | 1 | 152 | 173 (1)| | |
| 13 | NESTED LOOPS | | 1 | 148 | 173 (1)| | |
| 14 | NESTED LOOPS | | 1 | 126 | 170 (1)| | |
| 15 | NESTED LOOPS | | 1 | 122 | 170 (1)| | |
| 16 | NESTED LOOPS | | 1 | 97 | 169 (1)| | |
| 17 | NESTED LOOPS | | 1 | 53 | 167 (1)| | |
| 18 | SORT UNIQUE | | 1 | 21 | 22 (0)| | |
| 19 | TABLE ACCESS BY INDEX ROWID| GL_CODE_COMBINATIONS | 1 | 21 | 22 (0)| | |
| 20 | INDEX RANGE SCAN | GL_CODE_COMBINATIONS_N1 | 117 | | 8 (0)| | |
| 21 | TABLE ACCESS BY INDEX ROWID | GL_JE_LINES | 16 | 512 | 144 (0)| | |
| 22 | INDEX RANGE SCAN | GL_JE_LINES_N1 | 319 | | 4 (0)| | |
| 23 | TABLE ACCESS BY INDEX ROWID | GL_JE_HEADERS | 1 | 44 | 2 (0)| | |
| 24 | INDEX UNIQUE SCAN | GL_JE_HEADERS_U1 | 1 | | 1 (0)| | |
| 25 | INDEX RANGE SCAN | GL_PERIODS_U1 | 1 | 25 | 1 (0)| | |
| 26 | INDEX UNIQUE SCAN | GL_LEDGERS_U2 | 1 | 4 | 0 (0)| | |
| 27 | TABLE ACCESS BY INDEX ROWID | HR_ORGANIZATION_INFORMATION | 1 | 22 | 3 (0)| | |
| 28 | INDEX RANGE SCAN | HR_ORGANIZATION_INFORMATIO_FK1 | 30 | | 1 (0)| | |
| 29 | INDEX UNIQUE SCAN | HR_ORGANIZATION_UNITS_PK | 1 | 4 | 0 (0)| | |
| 30 | TABLE ACCESS BY INDEX ROWID | HR_ORGANIZATION_INFORMATION | 1 | 31 | 3 (0)| | |
| 31 | INDEX RANGE SCAN | HR_ORGANIZATION_INFORMATIO_FK2 | 2 | | 1 (0)| | |
| 32 | INDEX UNIQUE SCAN | HR_ALL_ORGANIZATION_UNTS_TL_PK | 1 | 24 | 0 (0)| | |
| 33 | TABLE ACCESS BY INDEX ROWID | GL_IMPORT_REFERENCES | 105 | 2415 | 58 (0)| | |
| 34 | INDEX RANGE SCAN | GL_IMPORT_REFERENCES_N1 | 105 | | 3 (0)| | |
| 35 | PARTITION LIST ALL | | 1 | 42 | 58 (0)| 1 | 19 |
| 36 | TABLE ACCESS BY LOCAL INDEX ROWID | XLA_AE_LINES | 1 | 42 | 58 (0)| 1 | 19 |
| 37 | INDEX RANGE SCAN | XLA_AE_LINES_N4 | 1 | | 57 (0)| 1 | 19 |
| 38 | INDEX UNIQUE SCAN | GL_JE_BATCHES_U1 | 1 | | 1 (0)| | |
| 39 | TABLE ACCESS BY INDEX ROWID | GL_JE_BATCHES | 1 | 7 | 2 (0)| | |
-------------------------------------------------------------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
Statistics
----------------------------------------------------------
99 recursive calls
0 db block gets
3451912 consistent gets
0 physical reads
0 redo size
345 bytes sent via SQL*Net to client
4385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed
AUTOTRACE for UNION ALL of full query that includes both request_1 and request_2
COUNT(*)
----------
58
Execution Plan
----------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 35 | 300 (1)| | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D6767_FC3F7B00 | | | | | |
| 3 | NESTED LOOPS | | 1 | 279 | 294 (1)| | |
| 4 | NESTED LOOPS | | 1 | 279 | 294 (1)| | |
| 5 | NESTED LOOPS | | 1 | 237 | 236 (1)| | |
| 6 | NESTED LOOPS | | 1 | 214 | 178 (1)| | |
| 7 | NESTED LOOPS | | 1 | 207 | 176 (1)| | |
| 8 | NESTED LOOPS | | 1 | 183 | 176 (1)| | |
| 9 | NESTED LOOPS | | 1 | 152 | 173 (1)| | |
| 10 | NESTED LOOPS | | 1 | 148 | 173 (1)| | |
| 11 | NESTED LOOPS | | 1 | 126 | 170 (1)| | |
| 12 | NESTED LOOPS | | 1 | 122 | 170 (1)| | |
| 13 | NESTED LOOPS | | 1 | 78 | 168 (1)| | |
| 14 | NESTED LOOPS | | 1 | 53 | 167 (1)| | |
| 15 | SORT UNIQUE | | 1 | 21 | 22 (0)| | |
| 16 | TABLE ACCESS BY INDEX ROWID| GL_CODE_COMBINATIONS | 1 | 21 | 22 (0)| | |
| 17 | INDEX RANGE SCAN | GL_CODE_COMBINATIONS_N1 | 117 | | 8 (0)| | |
| 18 | TABLE ACCESS BY INDEX ROWID | GL_JE_LINES | 16 | 512 | 144 (0)| | |
| 19 | INDEX RANGE SCAN | GL_JE_LINES_N1 | 319 | | 4 (0)| | |
| 20 | INDEX RANGE SCAN | GL_PERIODS_U1 | 1 | 25 | 1 (0)| | |
| 21 | TABLE ACCESS BY INDEX ROWID | GL_JE_HEADERS | 1 | 44 | 2 (0)| | |
| 22 | INDEX UNIQUE SCAN | GL_JE_HEADERS_U1 | 1 | | 1 (0)| | |
| 23 | INDEX UNIQUE SCAN | GL_LEDGERS_U2 | 1 | 4 | 0 (0)| | |
| 24 | TABLE ACCESS BY INDEX ROWID | HR_ORGANIZATION_INFORMATION | 1 | 22 | 3 (0)| | |
| 25 | INDEX RANGE SCAN | HR_ORGANIZATION_INFORMATIO_FK1 | 30 | | 1 (0)| | |
| 26 | INDEX UNIQUE SCAN | HR_ORGANIZATION_UNITS_PK | 1 | 4 | 0 (0)| | |
| 27 | TABLE ACCESS BY INDEX ROWID | HR_ORGANIZATION_INFORMATION | 1 | 31 | 3 (0)| | |
| 28 | INDEX RANGE SCAN | HR_ORGANIZATION_INFORMATIO_FK2 | 2 | | 1 (0)| | |
| 29 | INDEX UNIQUE SCAN | HR_ALL_ORGANIZATION_UNTS_TL_PK | 1 | 24 | 0 (0)| | |
| 30 | TABLE ACCESS BY INDEX ROWID | GL_JE_BATCHES | 1 | 7 | 2 (0)| | |
| 31 | INDEX UNIQUE SCAN | GL_JE_BATCHES_U1 | 1 | | 1 (0)| | |
| 32 | TABLE ACCESS BY INDEX ROWID | GL_IMPORT_REFERENCES | 105 | 2415 | 58 (0)| | |
| 33 | INDEX RANGE SCAN | GL_IMPORT_REFERENCES_N1 | 105 | | 3 (0)| | |
| 34 | PARTITION LIST ALL | | 1 | | 57 (0)| 1 | 19 |
| 35 | INDEX RANGE SCAN | XLA_AE_LINES_N4 | 1 | | 57 (0)| 1 | 19 |
| 36 | TABLE ACCESS BY LOCAL INDEX ROWID | XLA_AE_LINES | 1 | 42 | 58 (0)| 1 | 1 |
| 37 | SORT AGGREGATE | | 1 | 35 | | | |
| 38 | VIEW | | 2 | 70 | 6 (34)| | |
| 39 | UNION-ALL | | | | | | |
| 40 | COUNT | | | | | | |
| 41 | VIEW | | 1 | 35 | 3 (34)| | |
| 42 | HASH UNIQUE | | 1 | 44 | 3 (34)| | |
| 43 | VIEW | | 1 | 44 | 2 (0)| | |
| 44 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6767_FC3F7B00 | 1 | 74 | 2 (0)| | |
| 45 | COUNT | | | | | | |
| 46 | VIEW | | 1 | 35 | 3 (34)| | |
| 47 | HASH UNIQUE | | 1 | 61 | 3 (34)| | |
| 48 | VIEW | | 1 | 61 | 2 (0)| | |
| 49 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6767_FC3F7B00 | 1 | 74 | 2 (0)| | |
----------------------------------------------------------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
Statistics
----------------------------------------------------------
766 recursive calls
83076 db block gets
270487689 consistent gets
81660 physical reads
676 redo size
346 bytes sent via SQL*Net to client
4582 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed

As I don't know the underlying tables, their structure and their data, I can only guess. Having said that, I'd try the following things (not knowing if any of the steps are helpful or not!):
remove the rownum from receiving_all
add the where clause from the main query to each of the subqueries in receiving_all. The optimizer might be able to infer this, but I am not sure.
change union all to union in receiving_all and remove the distinct from receiving_1 and receiving_2
receiving_2 seems to be included in receiving_2?
If 4. is not the case, I would combine both into a single query with WHERE UPPER(bq.JE_CATEGORY) = UPPER('Receiving') AND ( (<conditions for 1>) OR (<conditions for 2>) )
I would try if UPPER(bq.JE_CATEGORY) can be avoided. Functions prevent index access.
In case you can create tables, I'd unroll the values of gccs into two tables, one for segment_1, and another table for segment_2.
If 7. is not an option, I'd add the hint select /*+ materialize */ gcc.code_combination_id to gcc.
I'm very curious to see which suggestion has any positive impact...

Related

How to optimize a complex oracle query using pagination

I need to get data from an oracle DB to paint some sort of matrix in an html table. The query I needed turned out to be very complex and slow, so I decided to implement pagination.
I followed many articles and posts, but the one I liked the most was this one: On rownum and limiting results
But it doesn't seem to work, the execution takes as long time as if there was no rows restriction, sometimes it even takes longer.
Please keep in mind than I'm new with oracle, I'm used to recent versions of MS SQL
Here's a brief example of what my query looks like:
SELECT * FROM (
SELECT /*+ FIRST_ROWS(n) */ a.*, ROWNUM rnum FROM (
WITH T1 AS (
SELECT ... FROM VIEW_1
INNER JOIN ...
WHERE ...
GROUP BY ...
),
T2 AS (
SELECT ... FROM VIEW_2
INNER JOIN ...
WHERE ...
GROUP BY ...
),
T3 AS (
SELECT ... FROM VIEW_3
LEFT JOIN ...
WHERE ...
GROUP BY ...
)
SELECT
T1.LVL,
T1.CRSE,
T2.ID,
T3.GRADE
FROM T1
INNER JOIN T2 ON ...
LEFT JOIN T3 ON ...
ORDER BY T2.ID DESC, T1.LVL, T1.CRSE) a
WHERE ROWNUM <= (PAGE * 50))
WHERE rnum >= ((PAGE-1) * 50);
My actual query is quite larger, and I mostly use views I don't have access to.
I don't know if the /*+ FIRST_ROWS(n) */ works like that... and also don't think I can set a variable there...
I was expecting the execution time to be much lower that without the ROWNUM check, but it just doesn't work, and I've read its because of the GROUP BY usage. Can someone please help me make my pagination faster?
EDIT:
Here's the EXPLAIN PLAN from my query, I hope it helps:
Plan hash value: 3394899150
-----------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3950 | 648K| | 17918 (1)| 00:03:36 |
|* 1 | VIEW | | 3950 | 648K| | 17918 (1)| 00:03:36 |
|* 2 | COUNT STOPKEY | | | | | | |
| 3 | VIEW | | 8424 | 1275K| | 17918 (1)| 00:03:36 |
| 4 | TEMP TABLE TRANSFORMATION | | | | | | |
| 5 | LOAD AS SELECT | SYS_TEMP_0FD9D6FD2_17D15EA9 | | | | | |
| 6 | HASH UNIQUE | | 36 | 2052 | | 250 (1)| 00:00:03 |
| 7 | VIEW | PS_UAG_ISBI_MAT_VW | 36 | 2052 | | 249 (1)| 00:00:03 |
|* 8 | HASH JOIN | | 36 | 3744 | | 249 (1)| 00:00:03 |
| 9 | NESTED LOOPS | | 34 | 2312 | | 14 (0)| 00:00:01 |
| 10 | NESTED LOOPS | | 44 | 2312 | | 14 (0)| 00:00:01 |
| 11 | NESTED LOOPS | | 1 | 46 | | 5 (0)| 00:00:01 |
| 12 | TABLE ACCESS BY INDEX ROWID | PS_ACAD_PROG_TBL | 1 | 29 | | 4 (0)| 00:00:01 |
|* 13 | INDEX SKIP SCAN | PS1ACAD_PROG_TBL | 1 | | | 3 (0)| 00:00:01 |
|* 14 | INDEX RANGE SCAN | PS1ACAD_ORG_TBL | 1 | 17 | | 1 (0)| 00:00:01 |
|* 15 | INDEX RANGE SCAN | PS2CRSE_OFFER | 44 | | | 1 (0)| 00:00:01 |
|* 16 | TABLE ACCESS BY INDEX ROWID | PS_CRSE_OFFER | 33 | 726 | | 9 (0)| 00:00:01 |
| 17 | VIEW | | 12227 | 429K| | 235 (1)| 00:00:03 |
|* 18 | HASH JOIN RIGHT OUTER | | 12227 | 573K| | 235 (1)| 00:00:03 |
|* 19 | INDEX FAST FULL SCAN | PS_CRSE_ATTRIBUTES | 7690 | 135K| | 98 (0)| 00:00:02 |
| 20 | TABLE ACCESS FULL | PS_CRSE_CATALOG | 12227 | 358K| | 136 (0)| 00:00:02 |
| 21 | LOAD AS SELECT | SYS_TEMP_0FD9D6FD3_17D15EA9 | | | | | |
| 22 | VIEW | | 682 | 36146 | | 1091 (1)| 00:00:14 |
|* 23 | HASH JOIN | | 682 | 41602 | | 1091 (1)| 00:00:14 |
|* 24 | TABLE ACCESS FULL | TYM_TRAYECTORIA | 682 | 17050 | | 513 (1)| 00:00:07 |
| 25 | INDEX FAST FULL SCAN | PS0PERSONAL_DATA | 255K| 8985K| | 577 (1)| 00:00:07 |
| 26 | LOAD AS SELECT | SYS_TEMP_0FD9D6FD4_17D15EA9 | | | | | |
| 27 | HASH GROUP BY | | 107 | 1605 | | 16164 (1)| 00:03:14 |
| 28 | VIEW | | 107 | 1605 | | 16163 (1)| 00:03:14 |
|* 29 | HASH JOIN | | 107 | 2461 | | 16163 (1)| 00:03:14 |
| 30 | VIEW | | 682 | 5456 | | 3 (0)| 00:00:01 |
| 31 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6FD3_17D15EA9 | 682 | 36146 | | 3 (0)| 00:00:01 |
| 32 | VIEW | PS_UAG_ISBI_CAL_VW | 6411 | 96165 | | 16160 (1)| 00:03:14 |
| 33 | SORT UNIQUE | | 6411 | 7004K| 1960K| 16160 (1)| 00:03:14 |
| 34 | UNION-ALL | | | | | | |
|* 35 | HASH JOIN | | 6342 | 1839K| | 15289 (1)| 00:03:04 |
|* 36 | INDEX FAST FULL SCAN | PS_CRSE_ATTRIBUTES | 7690 | 135K| | 98 (0)| 00:00:02 |
|* 37 | HASH JOIN | | 4943 | 1346K| | 15191 (1)| 00:03:03 |
|* 38 | HASH JOIN | | 4943 | 1168K| | 10790 (1)| 00:02:10 |
| 39 | INDEX FAST FULL SCAN | PS1TERM_TBL | 1256 | 31400 | | 4 (0)| 00:00:01 |
|* 40 | HASH JOIN | | 4943 | 1047K| | 10786 (1)| 00:02:10 |
| 41 | INDEX FAST FULL SCAN | PS0CRSE_CATALOG | 12227 | 358K| | 31 (0)| 00:00:01 |
|* 42 | HASH JOIN | | 4985 | 910K| | 10755 (1)| 00:02:10 |
| 43 | TABLE ACCESS FULL | PS_ACAD_ORG_TBL | 549 | 23058 | | 7 (0)| 00:00:01 |
|* 44 | HASH JOIN | | 5297 | 750K| 2856K| 10748 (1)| 00:02:09 |
| 45 | TABLE ACCESS FULL | PS_CLASS_TBL | 52112 | 2239K| | 650 (1)| 00:00:08 |
|* 46 | HASH JOIN | | 100K| 9893K| | 9422 (1)| 00:01:54 |
| 47 | TABLE ACCESS FULL | PS_ACAD_PROG_TBL | 313 | 18467 | | 5 (0)| 00:00:01 |
|* 48 | TABLE ACCESS FULL | PS_STDNT_ENRL | 986K| 39M| | 9414 (1)| 00:01:53 |
| 49 | TABLE ACCESS FULL | PS_PERSONAL_DATA | 255K| 9235K| | 4400 (1)| 00:00:53 |
| 50 | NESTED LOOPS | | 69 | 19596 | | 462 (1)| 00:00:06 |
| 51 | NESTED LOOPS | | 69 | 19596 | | 462 (1)| 00:00:06 |
|* 52 | HASH JOIN | | 69 | 17043 | | 324 (1)| 00:00:04 |
|* 53 | HASH JOIN | | 69 | 15318 | | 320 (1)| 00:00:04 |
|* 54 | HASH JOIN | | 105 | 21420 | | 222 (1)| 00:00:03 |
|* 55 | HASH JOIN | | 101 | 17574 | | 191 (1)| 00:00:03 |
|* 56 | HASH JOIN | | 101 | 14443 | | 88 (0)| 00:00:02 |
|* 57 | HASH JOIN | | 314 | 31714 | | 12 (0)| 00:00:01 |
| 58 | TABLE ACCESS FULL | PS_ACAD_PROG_TBL | 313 | 18467 | | 5 (0)| 00:00:01 |
| 59 | TABLE ACCESS FULL | PS_ACAD_ORG_TBL | 549 | 23058 | | 7 (0)| 00:00:01 |
| 60 | VIEW | | 7540 | 309K| | 76 (0)| 00:00:01 |
|* 61 | HASH JOIN | | 7540 | 493K| | 76 (0)| 00:00:01 |
| 62 | TABLE ACCESS FULL | PS_TRNS_CRSE_SCH | 597 | 16119 | | 6 (0)| 00:00:01 |
| 63 | TABLE ACCESS FULL | PS_TRNS_CRSE_DTL | 7540 | 294K| | 70 (0)| 00:00:01 |
| 64 | TABLE ACCESS FULL | PS_CRSE_OFFER | 12227 | 370K| | 102 (0)| 00:00:02 |
| 65 | INDEX FAST FULL SCAN | PS0CRSE_CATALOG | 12227 | 358K| | 31 (0)| 00:00:01 |
|* 66 | INDEX FAST FULL SCAN | PS_CRSE_ATTRIBUTES | 7690 | 135K| | 98 (0)| 00:00:02 |
| 67 | INDEX FAST FULL SCAN | PS1TERM_TBL | 1256 | 31400 | | 4 (0)| 00:00:01 |
|* 68 | INDEX UNIQUE SCAN | PS_PERSONAL_DATA | 1 | | | 1 (0)| 00:00:01 |
| 69 | TABLE ACCESS BY INDEX ROWID | PS_PERSONAL_DATA | 1 | 37 | | 2 (0)| 00:00:01 |
|* 70 | SORT ORDER BY STOPKEY | | 8424 | 1628K| 1736K| 413 (1)| 00:00:05 |
|* 71 | HASH JOIN RIGHT OUTER | | 8424 | 1628K| | 46 (5)| 00:00:01 |
| 72 | VIEW | | 108 | 2700 | | 2 (0)| 00:00:01 |
| 73 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6FD4_17D15EA9 | 108 | 1620 | | 2 (0)| 00:00:01 |
|* 74 | HASH JOIN RIGHT OUTER | | 78 | 13494 | | 44 (5)| 00:00:01 |
| 75 | VIEW | | 26 | 390 | | 24 (5)| 00:00:01 |
| 76 | HASH GROUP BY | | 26 | 702 | | 24 (5)| 00:00:01 |
| 77 | VIEW | | 47 | 1269 | | 24 (5)| 00:00:01 |
| 78 | HASH GROUP BY | | 47 | 2256 | | 24 (5)| 00:00:01 |
|* 79 | HASH JOIN OUTER | | 47 | 2256 | | 23 (0)| 00:00:01 |
|* 80 | HASH JOIN OUTER | | 47 | 1081 | | 21 (0)| 00:00:01 |
| 81 | VIEW | | 36 | 360 | | 2 (0)| 00:00:01 |
| 82 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6FD2_17D15EA9 | 36 | 2052 | | 2 (0)| 00:00:01 |
| 83 | TABLE ACCESS FULL | PS_RQ_GRP_DETL_TBL | 2702 | 35126 | | 19 (0)| 00:00:01 |
| 84 | VIEW | | 108 | 2700 | | 2 (0)| 00:00:01 |
| 85 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6FD4_17D15EA9 | 108 | 1620 | | 2 (0)| 00:00:01 |
|* 86 | HASH JOIN RIGHT OUTER | | 78 | 12324 | | 20 (5)| 00:00:01 |
| 87 | TABLE ACCESS FULL | PS_UAG_PRECARG_SUBJECT | 12 | 168 | | 3 (0)| 00:00:01 |
|* 88 | HASH JOIN RIGHT OUTER | | 78 | 11232 | | 17 (6)| 00:00:01 |
| 89 | VIEW | | 1 | 20 | | 12 (9)| 00:00:01 |
| 90 | HASH GROUP BY | | 1 | 30 | | 12 (9)| 00:00:01 |
| 91 | VIEW | PS_UAG_OFERCRSE_VW | 1 | 30 | | 12 (9)| 00:00:01 |
| 92 | HASH UNIQUE | | 1 | 213 | | 12 (9)| 00:00:01 |
| 93 | NESTED LOOPS OUTER | | 1 | 213 | | 11 (0)| 00:00:01 |
| 94 | NESTED LOOPS OUTER | | 1 | 177 | | 9 (0)| 00:00:01 |
| 95 | NESTED LOOPS OUTER | | 1 | 139 | | 6 (0)| 00:00:01 |
|* 96 | TABLE ACCESS BY INDEX ROWID| PS_CLASS_TBL | 1 | 69 | | 4 (0)| 00:00:01 |
|* 97 | INDEX RANGE SCAN | PSDCLASS_TBL | 1 | | | 3 (0)| 00:00:01 |
| 98 | TABLE ACCESS BY INDEX ROWID| PS_CLASS_MTG_PAT | 1 | 70 | | 2 (0)| 00:00:01 |
|* 99 | INDEX RANGE SCAN | PS_CLASS_MTG_PAT | 1 | | | 1 (0)| 00:00:01 |
| 100 | TABLE ACCESS BY INDEX ROWID | PS_CLASS_INSTR | 1 | 38 | | 3 (0)| 00:00:01 |
|*101 | INDEX RANGE SCAN | PS_CLASS_INSTR | 1 | | | 2 (0)| 00:00:01 |
| 102 | TABLE ACCESS BY INDEX ROWID | PS_PERSONAL_DATA | 1 | 36 | | 2 (0)| 00:00:01 |
|*103 | INDEX UNIQUE SCAN | PS_PERSONAL_DATA | 1 | | | 1 (0)| 00:00:01 |
| 104 | VIEW | | 78 | 9672 | | 5 (0)| 00:00:01 |
|*105 | HASH JOIN | | 78 | 10140 | | 5 (0)| 00:00:01 |
| 106 | VIEW | | 36 | 2988 | | 2 (0)| 00:00:01 |
| 107 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6FD2_17D15EA9 | 36 | 2052 | | 2 (0)| 00:00:01 |
| 108 | VIEW | | 682 | 32054 | | 3 (0)| 00:00:01 |
| 109 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6FD3_17D15EA9 | 682 | 36146 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RNUM">=0)
2 - filter(ROWNUM<=3950)
8 - access("D"."CRSE_ID"="C"."CRSE_ID")
13 - access("A"."ACAD_PROG"='LMC09')
filter("A"."ACAD_PROG"='LMC09')
14 - access("B"."INSTITUTION"="A"."INSTITUTION" AND "B"."ACAD_ORG"="A"."ACAD_ORG")
15 - access("C"."SUBJECT"="A"."ACAD_PLAN")
16 - filter("C"."INSTITUTION"="A"."INSTITUTION")
18 - access("E"."CRSE_ID"(+)="D"."CRSE_ID")
19 - filter("E"."CRSE_ATTR"(+)='0003')
23 - access("PERS"."EMPLID"="TRA"."EMPLID")
24 - filter("TRA"."STRM"='1835' AND "TRA"."ACAD_PLAN"='LMC09')
29 - access("A2"."EMPLID"="CAL"."EMPLID")
35 - access("F"."CRSE_ID"="E"."CRSE_ID")
36 - filter("F"."CRSE_ATTR"='0003')
37 - access("B"."EMPLID"="A"."EMPLID")
38 - access("H"."INSTITUTION"="A"."INSTITUTION" AND "H"."ACAD_CAREER"="A"."ACAD_CAREER" AND "H"."STRM"="A"."STRM")
40 - access("E"."CRSE_ID"="C"."CRSE_ID")
42 - access("G"."INSTITUTION"="A"."INSTITUTION" AND "G"."ACAD_ORG"="D"."ACAD_ORG")
44 - access("C"."INSTITUTION"="A"."INSTITUTION" AND "C"."ACAD_CAREER"="A"."ACAD_CAREER" AND "C"."STRM"="A"."STRM" AND
"C"."CLASS_NBR"="A"."CLASS_NBR" AND "C"."SESSION_CODE"="A"."SESSION_CODE")
46 - access("D"."INSTITUTION"="A"."INSTITUTION" AND "D"."ACAD_CAREER"="A"."ACAD_CAREER" AND
"D"."ACAD_PROG"="A"."ACAD_PROG")
48 - filter("A"."STDNT_ENRL_STATUS"='E')
52 - access("H"."INSTITUTION"="A"."INSTITUTION" AND "H"."ACAD_CAREER"="A"."ACAD_CAREER" AND
"H"."STRM"="A"."ARTICULATION_TERM")
53 - access("F"."CRSE_ID"="E"."CRSE_ID")
54 - access("E"."CRSE_ID"="C"."CRSE_ID")
55 - access("C"."INSTITUTION"="A"."INSTITUTION" AND "C"."ACAD_CAREER"="A"."ACAD_CAREER" AND "C"."CRSE_ID"="A"."CRSE_ID")
56 - access("D"."INSTITUTION"="A"."INSTITUTION" AND "D"."ACAD_CAREER"="A"."ACAD_CAREER" AND
"D"."ACAD_PROG"="A"."ACAD_PROG" AND "G"."INSTITUTION"="A"."INSTITUTION")
57 - access("G"."ACAD_ORG"="D"."ACAD_ORG")
61 - access("B"."EMPLID"="A"."EMPLID" AND "B"."ACAD_CAREER"="A"."ACAD_CAREER" AND "B"."INSTITUTION"="A"."INSTITUTION"
AND "B"."MODEL_NBR"="A"."MODEL_NBR")
66 - filter("F"."CRSE_ATTR"='0003')
68 - access("B"."EMPLID"="A"."EMPLID")
70 - filter(ROWNUM<=3950)
71 - access("C3"."CRSE_ID"(+)="from$_subquery$_019"."CRSE_ID" AND "C3"."EMPLID"(+)="from$_subquery$_019"."EMPLID")
74 - access("R3"."EMPLID"(+)="from$_subquery$_019"."EMPLID" AND
"R3"."RQRMNT_GROUP"(+)="from$_subquery$_019"."RQRMNT_GROUP")
filter("from$_subquery$_019"."RQRMNT_GROUP"<>CASE WHEN ("R3"."RQRMNT_GROUP"(+) IS NOT NULL) THEN ' ' ELSE ' ' END )
79 - access("REQ"."CRSE_ID"="C2"."CRSE_ID"(+))
80 - access("REQ"."RQRMNT_GROUP"(+)="M2"."RQRMNT_GROUP")
filter("M2"."RQRMNT_GROUP"<>CASE WHEN ("REQ"."RQRMNT_GROUP"(+) IS NOT NULL) THEN ' ' ELSE ' ' END )
86 - access("DTL"."CRSE_ID"(+)="M3"."CRSE_ID" AND "A3"."IDTRAYECTORIA"=TO_NUMBER("DTL"."ID_TRAYECTORIA"(+)))
88 - access("H3"."CRSE_ID"(+)="from$_subquery$_019"."CRSE_ID")
96 - filter("A"."SESSION_CODE"='ORD' AND "A"."CLASS_STAT"='A')
97 - access("A"."SUBJECT"='LMC09' AND "A"."STRM"='1853')
99 - access("B"."CRSE_ID"(+)="A"."CRSE_ID" AND "B"."CRSE_OFFER_NBR"(+)="A"."CRSE_OFFER_NBR" AND "B"."STRM"(+)='1853' AND
"B"."SESSION_CODE"(+)='ORD' AND "B"."CLASS_SECTION"(+)="A"."CLASS_SECTION")
101 - access("C"."CRSE_ID"(+)="B"."CRSE_ID" AND "C"."CRSE_OFFER_NBR"(+)="B"."CRSE_OFFER_NBR" AND "C"."STRM"(+)="B"."STRM"
AND "C"."SESSION_CODE"(+)="B"."SESSION_CODE" AND "C"."CLASS_SECTION"(+)="B"."CLASS_SECTION" AND
"C"."CLASS_MTG_NBR"(+)="B"."CLASS_MTG_NBR")
103 - access("D"."EMPLID"(+)="C"."EMPLID")
105 - access("M3"."ACAD_PLAN"="A3"."ACAD_PLAN")
EDIT 2, SOLUTION:
I Managed to find the indexes already configured for the table 'PS_STDNT_ENRL' using the following query:
SELECT
index_owner, index_name, table_name, column_name, column_position
FROM DBA_IND_COLUMNS
WHERE table_name = 'PS_STDNT_ENRL'
ORDER BY
index_owner,
table_name,
index_name,
column_position;
And found the columns needed on my WHERE clause so that the index works.
I modified my query and now its very fast.
ROWNUM only limits the number of rows presented AFTER the query runs and returns ALL rows. So it is still doing all of the work.
If t1.id is numeric, you can use MOD() to reduce the amount of work up front and easily chunk-up your results into as many sections as you desire. By way of example, MOD() of 5 against t1.id would allow you to break the results up into 5 sections corresponding to MOD() results of 0,1,2,3,4.
SELECT MOD(/*t1.id*/1000,5) FROM dual;
In this manner you reduce the number of rows considered in your driving table (t1) so there is less data to be managed after the other tables are joined in.
It would look something like this:
AND MOD(t1.id,5) =
00 -- Partition 1
-- 01 -- Partition 2
-- 02 -- Partition 3
-- 03 -- Partition 4
-- 04 -- Partition 5

Oracle - Do filter predicate order change the execution plan?

We are troubleshooting a performance problem - The application uses a VIEW and filter predicates applied as below. In the first case, result is retrieved in seconds,but the second one runs for more than an hour.
columns referred in the view actually point to table D. Please note that this view was written for some other purpose and now used for an enhancement(dont know why - management decision).
I can see that the problem is FTS on the partition tables E and F, but not able to understand why/how the order of predicates change the execution plan? this is a cost based approach only and statistics are upto date.
This is not a well formed query - using % eliminates indexes etc - but what puzzles me is why the plan changes when the statement is exchanging filter predicates
select COL2 from VIEW where COL3 like
'%180%' and COL4 LIKE '%Solu%'
Plan hash value: 1618822878
------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1379K(100)| | | |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
|* 1 | FILTER | | | | | | | |
| 2 | NESTED LOOPS OUTER | | 116K| 8568K| 1077K (19)| 02:47:19 | | |
|* 3 | HASH JOIN RIGHT OUTER | | 97611 | 6481K| 266K (1)| 00:41:25 | | |
|* 4 | INDEX FAST FULL SCAN | IDX1_A | 45156 | 352K| 25 (8)| 00:00:01 | | |
| 5 | NESTED LOOPS | | 97611 | 5719K| 266K (1)| 00:41:25 | | |
| 6 | NESTED LOOPS | | 98629 | 5719K| 266K (1)| 00:41:25 | | |
| 7 | VIEW | VW_NSO_1 | 98629 | 674K| 82 (3)| 00:00:01 | | |
| 8 | HASH UNIQUE | | 98629 | 1338K| 82 (3)| 00:00:01 | | |
| 9 | UNION-ALL | | | | | | | |
| 10 | TABLE ACCESS FULL | B | 97591 | 667K| 80 (3)| 00:00:01 | | |
| 11 | TABLE ACCESS FULL | C | 1038 | 2076 | 2 (0)| 00:00:01 | | |
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
|* 12 | INDEX UNIQUE SCAN | PK_D | 1 | | 2 (0)| 00:00:01 | | |
|* 13 | TABLE ACCESS BY GLOBAL INDEX ROWID| D | 1 | 53 | 3 (0)| 00:00:01 | ROWID | ROWID |
| 14 | VIEW | | 1 | 7 | 8 (25)| 00:00:01 | | |
| 15 | UNION ALL PUSHED PREDICATE | | | | | | | |
| 16 | SORT UNIQUE | | 1 | 9 | 4 (25)| 00:00:01 | | |
|* 17 | INDEX RANGE SCAN | IDX1_E | 1 | 9 | 3 (0)| 00:00:01 | | |
| 18 | SORT UNIQUE | | 1 | 9 | 5 (20)| 00:00:01 | | |
|* 19 | INDEX RANGE SCAN | F | 2 | 18 | 4 (0)| 00:00:01 | | |
|* 20 | TABLE ACCESS BY INDEX ROWID | G | 1 | 36 | 3 (0)| 00:00:01 | | |
|* 21 | INDEX UNIQUE SCAN | UK_G | 1 | | 2 (0)| 00:00:01 | | |
------------------------------------------------------------------------------------------------------------------------------------
select COL2 from VIEW where COL4 LIKE '%Solu%' AND COL3 like
'%180%' ;
Plan hash value: 2380952204
-----------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Pid | Ord | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | | 24 | SELECT STATEMENT | | | | | 2598K(100)| | | |
|* 1 | 0 | 23 | FILTER | | | | | | | | |
|* 2 | 1 | 20 | HASH JOIN OUTER | | 116K| 8568K| 7632K| 2295K (2)| 05:56:38 | | |
|* 3 | 2 | 11 | HASH JOIN OUTER | | 97611 | 6481K| 6864K| 266K (1)| 00:41:27 | | |
| 4 | 3 | 9 | NESTED LOOPS | | 97611 | 5719K| | 266K (1)| 00:41:25 | | |
| 5 | 4 | 7 | NESTED LOOPS | | 98629 | 5719K| | 266K (1)| 00:41:25 | | |
| 6 | 5 | 5 | VIEW | VW_NSO_1 | 98629 | 674K| | 82 (3)| 00:00:01 | | |
| 7 | 6 | 4 | HASH UNIQUE | | 98629 | 1338K| | 82 (3)| 00:00:01 | | |
| 8 | 7 | 3 | UNION-ALL | | | | | | | | |
| 9 | 8 | 1 | TABLE ACCESS FULL | B | 97591 | 667K| | 80 (3)| 00:00:01 | | |
| 10 | 8 | 2 | TABLE ACCESS FULL | C | 1038 | 2076 | | 2 (0)| 00:00:01 | | |
|* 11 | 5 | 6 | INDEX UNIQUE SCAN | PK_D | 1 | | | 2 (0)| 00:00:01 | | |
|* 12 | 4 | 8 | TABLE ACCESS BY GLOBAL INDEX ROWID| D | 1 | 53 | | 3 (0)| 00:00:01 | ROWID | ROWID |
|* 13 | 3 | 10 | TABLE ACCESS FULL | A | 45156 | 352K| | 75 (3)| 00:00:01 | | |
| 14 | 2 | 19 | VIEW | | 127M| 851M| | 1988K (3)| 05:08:49 | | |
| 15 | 14 | 18 | UNION-ALL | | | | | | | | |
| 16 | 15 | 14 | HASH UNIQUE | | 21M| 184M| 412M| 235K (4)| 00:36:33 | | |
| 17 | 16 | 13 | PARTITION RANGE ALL | | 21M| 184M| | 164K (4)| 00:25:30 | 1 | 32 |
| 18 | 17 | 12 | TABLE ACCESS FULL | E | 21M| 184M| | 164K (4)| 00:25:30 | 1 | 32 |
| 19 | 15 | 17 | HASH UNIQUE | | 106M| 910M| 4569M| 1752K (2)| 04:32:17 | | |
| 20 | 19 | 16 | PARTITION RANGE ALL | | 238M| 2048M| | 1286K (1)| 03:19:54 | 1 | 32 |
| 21 | 20 | 15 | TABLE ACCESS FULL | F | 238M| 2048M| | 1286K (1)| 03:19:54 | 1 | 32 |
|* 22 | 1 | 22 | TABLE ACCESS BY INDEX ROWID | G | 1 | 36 | | 3 (0)| 00:00:01 | | |
|* 23 | 22 | 21 | INDEX UNIQUE SCAN | UK_G | 1 | | | 2 (0)| 00:00:01 | | |
-----------------------------------------------------------------------------------------------------------------------------------------------------

Complicated Oracle Grouping Composite CUBE Query

I need to use the CUBE grouping function in order to get every possible combination so that this report can be used on APEX with parameters controlling how the report views. My full query is too complicated to show and will probably just confuse the situation. here I have the cutdown version
Select /*+ NO_PARALLEL */ ETP_ETPC_UID,
Decode (Grouping(ETP_ET_UID), 1, '-Grouped-', ETP_ET_UID),
/* Decode (Grouping(ETP_ET_UID), 1, '-Grouped-', ET_ABBR), */
Decode (Grouping(ETP_REFERENCE_1), 1, '-Grouped-', ETP_REFERENCE_1),
Decode (Grouping(ETP_REFERENCE_2), 1, '-Grouped-', ETP_REFERENCE_2),
Sum(ETP_COUNT)
From ETP_PROFILE,
ET_EVENT_TYPE
Where ETP_ET_UID = ET_UID
Group By
ETP_ETPC_UID,
Cube( /*(*/ETP_ET_UID/*, ET_ABBR)*/ , ETP_REFERENCE_1, ETP_REFERENCE_2)
The issue I am currently having is to do with the sections I have commented out. According to this Oracle Base article the composite column is meant to be treated as 1, so great, I can have the ET_ABBR in my query and just set ET_UID and ET_ABBR as a composite (un-comment my code and you will see).
When I do this, it seems to ridiculously increase the cost of the query (according to explain plan) and indeed it takes forever to run. If I remove my ET_ABBR column (how the code is right now with comments in place) it loads very quick and Explain plan cost is great.
Am I doing something wrong here, should I be using Grouping Sets or something like that? This is the first time I am messing with these special grouping commands, and they seem to be good, but very confusing.
EDIT:
Explain plan for Commented Query:
Plan hash value: 3169115854
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 142K| 9M| 408 (3)| 00:00:05 | | |
| 1 | SORT GROUP BY | | 142K| 9M| 408 (3)| 00:00:05 | | |
| 2 | GENERATE CUBE | | 142K| 9M| 408 (3)| 00:00:05 | | |
| 3 | SORT GROUP BY | | 142K| 9M| 408 (3)| 00:00:05 | | |
| 4 | PARTITION RANGE ALL| | 142K| 9M| 401 (1)| 00:00:05 | 1 |1048575|
| 5 | PARTITION LIST ALL| | 142K| 9M| 401 (1)| 00:00:05 | 1 | 10 |
| 6 | TABLE ACCESS FULL| ETP_PROFILE | 142K| 9M| 401 (1)| 00:00:05 | 1 |1048575|
------------------------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Explain Plan for un-commented code:
Plan hash value: 2063641247
--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 54 | 6426 | 427 (4)| 00:00:06 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | MULTI-TABLE INSERT | | | | | | | |
| 3 | SORT GROUP BY ROLLUP | | 54 | 4536 | 412 (3)| 00:00:05 | | |
|* 4 | HASH JOIN | | 142K| 11M| 406 (1)| 00:00:05 | | |
| 5 | TABLE ACCESS FULL | ET_EVENT_TYPE | 55 | 605 | 4 (0)| 00:00:01 | | |
| 6 | PARTITION RANGE ALL | | 142K| 9M| 401 (1)| 00:00:05 | 1 |1048575|
| 7 | PARTITION LIST ALL | | 142K| 9M| 401 (1)| 00:00:05 | 1 | 10 |
| 8 | TABLE ACCESS FULL | ETP_PROFILE | 142K| 9M| 401 (1)| 00:00:05 | 1 |1048575|
| 9 | DIRECT LOAD INTO | SYS_TEMP_0FD9D6E6F_E9BC1839 | | | | | | |
| 10 | DIRECT LOAD INTO | SYS_TEMP_0FD9D6E70_E9BC1839 | | | | | | |
| 11 | LOAD AS SELECT | SYS_TEMP_0FD9D6E70_E9BC1839 | | | | | | |
| 12 | SORT GROUP BY ROLLUP | | 54 | 3402 | 3 (34)| 00:00:01 | | |
| 13 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E6F_E9BC1839 | 54 | 3402 | 2 (0)| 00:00:01 | | |
| 14 | MULTI-TABLE INSERT | | | | | | | |
| 15 | SORT GROUP BY ROLLUP | | 54 | 3240 | 3 (34)| 00:00:01 | | |
| 16 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E6F_E9BC1839 | 54 | 3240 | 2 (0)| 00:00:01 | | |
| 17 | DIRECT LOAD INTO | SYS_TEMP_0FD9D6E71_E9BC1839 | | | | | | |
| 18 | DIRECT LOAD INTO | SYS_TEMP_0FD9D6E70_E9BC1839 | | | | | | |
| 19 | LOAD AS SELECT | SYS_TEMP_0FD9D6E70_E9BC1839 | | | | | | |
| 20 | SORT GROUP BY ROLLUP | | 54 | 2322 | 3 (34)| 00:00:01 | | |
| 21 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E71_E9BC1839 | 54 | 2322 | 2 (0)| 00:00:01 | | |
| 22 | VIEW | | 162 | 19278 | 6 (0)| 00:00:01 | | |
| 23 | VIEW | | 162 | 15066 | 6 (0)| 00:00:01 | | |
| 24 | UNION-ALL | | | | | | | |
| 25 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E6F_E9BC1839 | 54 | 4536 | 2 (0)| 00:00:01 | | |
| 26 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E70_E9BC1839 | 54 | 4536 | 2 (0)| 00:00:01 | | |
| 27 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E71_E9BC1839 | 54 | 3240 | 2 (0)| 00:00:01 | | |
--------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("SYS_TBL_$2$"."ETP_ET_UID"="SYS_TBL_$1$"."ET_UID")
Note
-----
- dynamic sampling used for this statement (level=2)

Oracle "Total" plan cost is really less than some of it's elements

I cannot figure out why sometimes, the total cost of a plan can be a very small number whereas looking inside the plan we can find huge costs. (indeed the query is very slow).
Can somebody explain me that?
Here is an example.
Apparently the costful part comes from a field in the main select that does a listagg on a subview and the join condition with this subview contains a complex condition (we can join on one field or another).
| Id | Operation | Name | Rows | Bytes | Cost |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 875 | 20 |
| 1 | SORT GROUP BY | | 1 | 544 | |
| 2 | VIEW | | 1 | 544 | 3 |
| 3 | SORT UNIQUE | | 1 | 481 | 3 |
| 4 | NESTED LOOPS | | | | |
| 5 | NESTED LOOPS | | 3 | 1443 | 2 |
| 6 | TABLE ACCESS BY INDEX ROWID | | 7 | 140 | 1 |
| 7 | INDEX RANGE SCAN | | 7 | | 1 |
| 8 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 9 | TABLE ACCESS BY INDEX ROWID | | 1 | 461 | 1 |
| 10 | SORT GROUP BY | | 1 | 182 | |
| 11 | NESTED LOOPS | | | | |
| 12 | NESTED LOOPS | | 8 | 1456 | 3 |
| 13 | NESTED LOOPS | | 8 | 304 | 2 |
| 14 | TABLE ACCESS BY INDEX ROWID | | 7 | 154 | 1 |
| 15 | INDEX RANGE SCAN | | 7 | | 1 |
| 16 | INDEX RANGE SCAN | | 1 | 16 | 1 |
| 17 | INDEX RANGE SCAN | | 1 | | 1 |
| 18 | TABLE ACCESS BY INDEX ROWID | | 1 | 144 | 1 |
| 19 | SORT GROUP BY | | 1 | 268 | |
| 20 | VIEW | | 1 | 268 | 9 |
| 21 | SORT UNIQUE | | 1 | 108 | 9 |
| 22 | CONCATENATION | | | | |
| 23 | NESTED LOOPS | | | | |
| 24 | NESTED LOOPS | | 1 | 108 | 4 |
| 25 | NESTED LOOPS | | 1 | 79 | 3 |
| 26 | NESTED LOOPS | | 1 | 59 | 2 |
| 27 | TABLE ACCESS BY INDEX ROWID | | 1 | 16 | 1 |
| 28 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 29 | TABLE ACCESS BY INDEX ROWID | | 1 | 43 | 1 |
| 30 | INDEX RANGE SCAN | | 1 | | 1 |
| 31 | TABLE ACCESS BY INDEX ROWID | | 1 | 20 | 1 |
| 32 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 33 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 34 | TABLE ACCESS BY INDEX ROWID | | 1 | 29 | 1 |
| 35 | NESTED LOOPS | | | | |
| 36 | NESTED LOOPS | | 1 | 108 | 4 |
| 37 | NESTED LOOPS | | 1 | 79 | 3 |
| 38 | NESTED LOOPS | | 1 | 59 | 2 |
| 39 | TABLE ACCESS BY INDEX ROWID | | 4 | 64 | 1 |
| 40 | INDEX RANGE SCAN | | 2 | | 1 |
| 41 | TABLE ACCESS BY INDEX ROWID | | 1 | 43 | 1 |
| 42 | INDEX RANGE SCAN | | 1 | | 1 |
| 43 | TABLE ACCESS BY INDEX ROWID | | 1 | 20 | 1 |
| 44 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 45 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 46 | TABLE ACCESS BY INDEX ROWID | | 1 | 29 | 1 |
| 47 | SORT GROUP BY | | 1 | 330 | |
| 48 | VIEW | | 1 | 330 | 26695 |
| 49 | SORT UNIQUE | | 1 | 130 | 26695 |
| 50 | CONCATENATION | | | | |
| 51 | HASH JOIN ANTI | | 1 | 130 | 13347 |
| 52 | NESTED LOOPS | | | | |
| 53 | NESTED LOOPS | | 1 | 110 | 4 |
| 54 | NESTED LOOPS | | 1 | 81 | 3 |
| 55 | NESTED LOOPS | | 1 | 61 | 2 |
| 56 | TABLE ACCESS BY INDEX ROWID | | 1 | 16 | 1 |
| 57 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 58 | TABLE ACCESS BY INDEX ROWID | | 1 | 45 | 1 |
| 59 | INDEX RANGE SCAN | | 1 | | 1 |
| 60 | TABLE ACCESS BY INDEX ROWID | | 1 | 20 | 1 |
| 61 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 62 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 63 | TABLE ACCESS BY INDEX ROWID | | 1 | 29 | 1 |
| 64 | VIEW | | 164K| 3220K| 13341 |
| 65 | NESTED LOOPS | | | | |
| 66 | NESTED LOOPS | | 164K| 11M| 13341 |
| 67 | NESTED LOOPS | | 164K| 8535K| 10041 |
| 68 | TABLE ACCESS BY INDEX ROWID | | 164K| 6924K| 8391 |
| 69 | INDEX SKIP SCAN | | 2131K| | 163 |
| 70 | INDEX UNIQUE SCAN | | 1 | 10 | 1 |
| 71 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 72 | TABLE ACCESS BY INDEX ROWID | | 1 | 20 | 1 |
| 73 | HASH JOIN ANTI | | 2 | 260 | 13347 |
| 74 | NESTED LOOPS | | | | |
| 75 | NESTED LOOPS | | 2 | 220 | 4 |
| 76 | NESTED LOOPS | | 2 | 162 | 3 |
| 77 | NESTED LOOPS | | 2 | 122 | 2 |
| 78 | TABLE ACCESS BY INDEX ROWID | | 4 | 64 | 1 |
| 79 | INDEX RANGE SCAN | | 2 | | 1 |
| 80 | TABLE ACCESS BY INDEX ROWID | | 1 | 45 | 1 |
| 81 | INDEX RANGE SCAN | | 1 | | 1 |
| 82 | TABLE ACCESS BY INDEX ROWID | | 1 | 20 | 1 |
| 83 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 84 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 85 | TABLE ACCESS BY INDEX ROWID | | 1 | 29 | 1 |
| 86 | VIEW | | 164K| 3220K| 13341 |
| 87 | NESTED LOOPS | | | | |
| 88 | NESTED LOOPS | | 164K| 11M| 13341 |
| 89 | NESTED LOOPS | | 164K| 8535K| 10041 |
| 90 | TABLE ACCESS BY INDEX ROWID | | 164K| 6924K| 8391 |
| 91 | INDEX SKIP SCAN | | 2131K| | 163 |
| 92 | INDEX UNIQUE SCAN | | 1 | 10 | 1 |
| 93 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 94 | TABLE ACCESS BY INDEX ROWID | | 1 | 20 | 1 |
| 95 | NESTED LOOPS OUTER | | 1 | 875 | 20 |
| 96 | NESTED LOOPS OUTER | | 1 | 846 | 19 |
| 97 | NESTED LOOPS OUTER | | 1 | 800 | 18 |
| 98 | NESTED LOOPS OUTER | | 1 | 776 | 17 |
| 99 | NESTED LOOPS OUTER | | 1 | 752 | 16 |
| 100 | NESTED LOOPS OUTER | | 1 | 641 | 15 |
| 101 | NESTED LOOPS OUTER | | 1 | 576 | 14 |
| 102 | NESTED LOOPS OUTER | | 1 | 554 | 13 |
| 103 | NESTED LOOPS OUTER | | 1 | 487 | 12 |
| 104 | NESTED LOOPS OUTER | | 1 | 434 | 11 |
| 105 | NESTED LOOPS | | 1 | 368 | 10 |
| 106 | NESTED LOOPS | | 1 | 102 | 9 |
| 107 | NESTED LOOPS OUTER | | 1 | 85 | 8 |
| 108 | NESTED LOOPS | | 1 | 68 | 7 |
| 109 | NESTED LOOPS | | 50 | 2700 | 6 |
| 110 | HASH JOIN | | 53 | 1696 | 5 |
| 111 | INLIST ITERATOR | | | | |
| 112 | TABLE ACCESS BY INDEX ROWID| | 520 | 10400 | 3 |
| 113 | INDEX RANGE SCAN | | 520 | | 1 |
| 114 | INLIST ITERATOR | | | | |
| 115 | TABLE ACCESS BY INDEX ROWID| | 91457 | 1071K| 1 |
| 116 | INDEX UNIQUE SCAN | | 2 | | 1 |
| 117 | TABLE ACCESS BY INDEX ROWID | | 1 | 22 | 1 |
| 118 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 119 | TABLE ACCESS BY INDEX ROWID | | 1 | 14 | 1 |
| 120 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 121 | TABLE ACCESS BY INDEX ROWID | | 1 | 17 | 1 |
| 122 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 123 | TABLE ACCESS BY INDEX ROWID | | 1 | 17 | 1 |
| 124 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 125 | TABLE ACCESS BY INDEX ROWID | | 1 | 266 | 1 |
| 126 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 127 | TABLE ACCESS BY INDEX ROWID | | 1 | 66 | 1 |
| 128 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 129 | TABLE ACCESS BY INDEX ROWID | | 1 | 53 | 1 |
| 130 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 131 | TABLE ACCESS BY INDEX ROWID | | 1 | 67 | 1 |
| 132 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 133 | INDEX RANGE SCAN | | 1 | 22 | 1 |
| 134 | TABLE ACCESS BY INDEX ROWID | | 1 | 65 | 1 |
| 135 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 136 | TABLE ACCESS BY INDEX ROWID | | 1 | 111 | 1 |
| 137 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 138 | TABLE ACCESS BY INDEX ROWID | | 1 | 24 | 1 |
| 139 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 140 | TABLE ACCESS BY INDEX ROWID | | 1 | 24 | 1 |
| 141 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 142 | TABLE ACCESS BY INDEX ROWID | | 1 | 46 | 1 |
| 143 | INDEX UNIQUE SCAN | | 1 | | 1 |
| 144 | TABLE ACCESS BY INDEX ROWID | | 1 | 29 | 1 |
| 145 | INDEX UNIQUE SCAN | | 1 | | 1 |
----------------------------------------------------------------------------------------------------------
The total cost of a statement is usually equal to or greater than the cost of any of its child operations. There are at least 4 exceptions to this rule.
Your plan looks like #3 but we can't be sure without looking at code.
1. FILTER
Execution plans may depend on conditions at run-time. These conditions cause FILTER operations that will dynamically decide which query block to execute. The example below uses a static condition but still demonstrates the concept. Part of the subquery is very expensive but the condition negates the whole thing.
explain plan for select * from dba_objects cross join dba_objects where 1 = 2;
select * from table(dbms_xplan.display(format => 'basic +cost'));
Plan hash value: 3258663795
--------------------------------------------------------------------
| Id | Operation | Name | Cost (%CPU)|
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 0 (0)|
| 1 | FILTER | | |
| 2 | MERGE JOIN CARTESIAN | | 11M (3)|
...
2. COUNT STOPKEY
Execution plans sum child operations up until the final cost. But child operations will not always finish. In the example below it may be correct to say that part of the plan costs 214. But because of the condition where rownum <= 1 only part of that child operation may run.
explain plan for
select /*+ no_query_transformation */ *
from (select * from dba_objects join dba_objects using (owner))
where rownum <= 1;
select * from table(dbms_xplan.display(format => 'basic +cost'));
Plan hash value: 2132093199
-------------------------------------------------------------------------------
| Id | Operation | Name | Cost (%CPU)|
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 (0)|
| 1 | COUNT STOPKEY | | |
| 2 | VIEW | | 4 (0)|
| 3 | VIEW | | 4 (0)|
| 4 | NESTED LOOPS | | 4 (0)|
| 5 | VIEW | DBA_OBJECTS | 2 (0)|
| 6 | UNION-ALL | | |
| 7 | HASH JOIN | | 3 (34)|
| 8 | INDEX FULL SCAN | I_USER2 | 1 (0)|
| 9 | VIEW | _CURRENT_EDITION_OBJ | 1 (0)|
| 10 | FILTER | | |
| 11 | HASH JOIN | | 214 (3)|
...
3. Subqueries in the SELECT column list
Cost aggregation does not include subqueries in the SELECT column list. A query like select ([expensive query]) from dual; will have a very small total cost. I don't understand the reason for this; Oracle estimates the subquery and he number of rows in the FROM, surely it could multiply them together for a total cost.
explain plan for
select dummy,(select count(*) from dba_objects cross join dba_objects) from dual;
select * from table(dbms_xplan.display(format => 'basic +cost'));
Plan hash value: 3705842531
---------------------------------------------------------------
| Id | Operation | Name | Cost (%CPU)|
---------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 (0)|
| 1 | SORT AGGREGATE | | |
| 2 | MERGE JOIN CARTESIAN | | 11M (3)|
...
4. Other - rounding? bugs?
About 0.01% of plans still have unexplainable cost issues. I can't find any pattern among them. Perhaps it's just a rounding issue or some rare optimizer bugs. There will always be some weird cases with a any model as complicated as the optimizer.
Check for more exceptions
This query can find other exceptions, it returns all plans where the first cost is less than the maximum cost.
select *
from
(
--First and Max cost per plan.
select
sql_id, plan_hash_value, id, cost
,max(cost) keep (dense_rank first order by id)
over (partition by sql_id, plan_hash_value) first_cost
,max(cost)
over (partition by sql_id, plan_hash_value) max_cost
,max(case when operation = 'COUNT' and options = 'STOPKEY' then 1 else 0 end)
over (partition by sql_id, plan_hash_value) has_count_stopkey
,max(case when operation = 'FILTER' and options is null then 1 else 0 end)
over (partition by sql_id, plan_hash_value) has_filter
,count(distinct(plan_hash_value))
over () total_plans
from v$sql_plan
--where sql_id = '61a161nm1ttjj'
order by 1,2,3
)
where first_cost < max_cost
--It's easy to exclude FILTER and COUNT STOPKEY.
and has_filter = 0
and has_count_stopkey = 0
order by 1,2,3;

Oracle 10g database optimization

Sorry for my English.
I have so big DB on Oracle 10g.
And I have queries, which executed so slow. For example, I have query :
115559 rows – 102 seconds
Execution plan:
Plan hash value: 3664903106
------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | SORT ORDER BY | | 1 | 30503 | 50 |00:00:09.33 | 3989 | 33M| 2065K| 29M (0)|
|* 2 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:05.71 | 3989 | 862K| 862K| 1186K (0)|
| 3 | INDEX FAST FULL SCAN | IX2_SLCND | 1 | 1387 | 1387 |00:00:00.01 | 17 | | | |
|* 4 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:05.47 | 3972 | 785K| 785K| 1145K (0)|
| 5 | TABLE ACCESS FULL | DISC | 1 | 90 | 90 |00:00:00.01 | 7 | | | |
|* 6 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:05.24 | 3965 | 776K| 776K| 403K (0)|
| 7 | VIEW | index$_join$_030 | 1 | 2 | 2 |00:00:00.01 | 6 | | | |
|* 8 | HASH JOIN | | 1 | | 2 |00:00:00.01 | 6 | 760K| 760K| 464K (0)|
| 9 | INDEX FAST FULL SCAN | IX_CMP_NAME | 1 | 2 | 2 |00:00:00.01 | 3 | | | |
| 10 | INDEX FAST FULL SCAN | PK_CMP | 1 | 2 | 2 |00:00:00.01 | 3 | | | |
|* 11 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:05.01 | 3959 | 756K| 756K| 1066K (0)|
| 12 | TABLE ACCESS FULL | TTYPE | 1 | 26 | 27 |00:00:00.01 | 7 | | | |
|* 13 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:04.78 | 3952 | 767K| 767K| 416K (0)|
| 14 | TABLE ACCESS FULL | COMMTCOMP | 1 | 2 | 2 |00:00:00.01 | 7 | | | |
|* 15 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:04.54 | 3945 | 760K| 760K| 1037K (0)|
| 16 | TABLE ACCESS FULL | TTYPE | 1 | 26 | 27 |00:00:00.01 | 7 | | | |
|* 17 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:04.31 | 3938 | 842K| 842K| 1267K (0)|
| 18 | TABLE ACCESS FULL | SLRSSEASONTC | 1 | 2885 | 2974 |00:00:00.01 | 53 | | | |
|* 19 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:04.07 | 3885 | 909K| 909K| 1178K (0)|
| 20 | TABLE ACCESS FULL | CAR | 1 | 976 | 976 |00:00:00.01 | 16 | | | |
|* 21 | HASH JOIN RIGHT OUTER | | 1 | 30503 | 115K|00:00:03.84 | 3869 | 1036K| 1036K| 1169K (0)|
| 22 | INDEX FULL SCAN | PK_STP | 1 | 258 | 258 |00:00:00.01 | 1 | | | |
|* 23 | HASH JOIN | | 1 | 30503 | 115K|00:00:03.60 | 3868 | 778K| 778K| 1165K (0)|
| 24 | TABLE ACCESS FULL | WKST | 1 | 83 | 83 |00:00:00.01 | 7 | | | |
|* 25 | HASH JOIN | | 1 | 61027 | 115K|00:00:03.37 | 3861 | 804K| 804K| 924K (0)|
| 26 | VIEW | index$_join$_014 | 1 | 12 | 12 |00:00:00.01 | 6 | | | |
|* 27 | HASH JOIN | | 1 | | 12 |00:00:00.01 | 6 | 783K| 783K| 919K (0)|
| 28 | INDEX FAST FULL SCAN | IX_BRANCH_NAME | 1 | 12 | 12 |00:00:00.01 | 3 | | | |
| 29 | INDEX FAST FULL SCAN | PK_BRANCH | 1 | 12 | 12 |00:00:00.01 | 3 | | | |
|* 30 | HASH JOIN | | 1 | 61027 | 115K|00:00:03.13 | 3855 | 808K| 808K| 1166K (0)|
| 31 | TABLE ACCESS FULL | USERS | 1 | 96 | 96 |00:00:00.01 | 16 | | | |
| 32 | MERGE JOIN | | 1 | 115K| 115K|00:00:02.90 | 3839 | | | |
|* 33 | HASH JOIN RIGHT OUTER| | 1 | 115K| 115K|00:00:02.32 | 3838 | 848K| 848K| 1169K (0)|
| 34 | TABLE ACCESS FULL | STP | 1 | 258 | 258 |00:00:00.01 | 7 | | | |
|* 35 | HASH JOIN | | 1 | 115K| 115K|00:00:01.95 | 3831 | 848K| 848K| 1169K (0)|
| 36 | TABLE ACCESS FULL | STP | 1 | 258 | 258 |00:00:00.01 | 7 | | | |
|* 37 | HASH JOIN | | 1 | 115K| 115K|00:00:01.52 | 3824 | 848K| 848K| 1169K (0)|
| 38 | TABLE ACCESS FULL | STP | 1 | 258 | 258 |00:00:00.01 | 7 | | | |
|* 39 | HASH JOIN | | 1 | 115K| 115K|00:00:01.09 | 3817 | 10M| 1956K| 12M (0)|
|* 40 | TABLE ACCESS FULL| SL | 1 | 116K| 120K|00:00:00.01 | 2115 | | | |
|* 41 | TABLE ACCESS FULL| SLTC | 1 | 114K| 115K|00:00:00.19 | 1702 | | | |
|* 42 | SORT JOIN | | 115K| 1 | 115K|00:00:00.27 | 1 | 73728 | 73728 | |
|* 43 | INDEX UNIQUE SCAN | PK_CMP | 1 | 1 | 1 |00:00:00.01 | 1 | | | |
------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("SLCND"."ID"="SL"."ID")
4 - access("SL"."DISC_ID"="DISC"."ID")
6 - access("CARI"."ID"="SLTC"."CARI_ID")
8 - access(ROWID=ROWID)
11 - access("TTYPE"."ID"="SLTC"."TTYPE_ID")
13 - access("CTC"."ID"="SLRSSEASONTC "."ID_COMMTCOMP")
15 - access("RS"."ID"="SLRSSEASONTC "."COMMTTYPE_ID")
17 - access("SLRSSEASONTC "."ID"="SLTC"."ID")
19 - access("CAR"."ID"="SLTC"."CAR_ID")
21 - access("STP"."ID"="WKST"."DEF_STP_ID")
23 - access("SL"."POS_WKST_ID"="WKST"."ID")
25 - access("USERS"."BRANCH_ID"="BRANCH"."ID")
27 - access(ROWID=ROWID)
30 - access("SL"."USR_CAS_ID"="USERS"."ID")
33 - access("SLTC"."THSTP_ID"="THSTP"."ID")
35 - access("SLTC"."TSTP_ID"="TSTP"."ID")
37 - access("SLTC"."FSTP_ID"="FSTP"."ID")
39 - access("SLTC"."ID"="SL"."ID")
40 - filter(("SL"."WHEN_DATE">=TO_DATE('2001-10-29 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND "SL"."SCOMP_ID"=1 AND
"SL"."WHEN_DATE"<=TO_DATE('2013-10-29 23:59:59', 'yyyy-mm-dd hh24:mi:ss')))
41 - filter("SLTC"."MANUAL"='N')
42 - access("SL"."SCOMP_ID"="CMP"."ID")
filter("SL"."SCOMP_ID"="CMP"."ID")
43 - access("CMP"."ID"=1)
I need ideas to make db faster.
Thanks You!
p.s.
I’m new in databases
So yeah, a you have a pretty horrible execution plan there.
To optimise the query, we would need the query as well, but I'll try to give you a few hints using just your exection plan output.
See all the TABLE ACCESS FULL? This is what usually takes time! Try using indexes on your tables, it seems that there are too few indexes used here (probably because no other indexes exist). Based on the execution plan, here are some simple indexes that might work, you may try them out:
CREATE INDEX ix_sltc_id ON sltc(ID);
CREATE INDEX ix_sl_id ON sl(ID);
CREATE INDEX ix_sltc_manual ON sltc(manual);
CREATE INDEX ix_sl_scomp_id ON sl(SCOMP_ID);
CREATE INDEX ix_sl_pos_wkst_id ON sl(POS_WKST_ID);
CREATE INDEX ix_sltc_thstp_id ON sltc(THSTP_ID)
CREATE INDEX ix_sltc_thstp_id ON sltc(THSTP_ID);
CREATE INDEX ix_sltc_tstp_id ON sltc(TSTP_ID);
CREATE INDEX ix_sltc_fstp_id ON sltc(FSTP_ID);
Without the query itself, we are quite limited here and probably missing most of the picture. Using the query, we could then try to create composite indexes, that are much more efficient.
More importantly, it seems that you do not use PRIMARY KEYs on your tables, because there are a lot of TABLE ACCESS FULL operations on tables with the predicate "ID". Make sure all your "ID" columns are indexed as well (or defined as PRIMARY KEY if that is indeed the primary key).
Also, we see that the optimizer does not have correct statistics, try to manually refresh the statistics for your schema:
EXEC DBMS_STATS.delete_schema_stats('SCOTT');
And as a sidenote: Whoever designed this schema, educate this person on database design!
Another sidenote for the more advanced readers: While TABLE ACCESS FULL is not always bad (there are quite a few cases where a full table scan is faster than access via indexes), it often indicates missing indexes. So setting the indexes and refreshing the statistics should provide a performance gain here.

Resources