Oracle performance lowered by using parenthesis - sql

I've read this: Parenthesis() and SQL Query Performance
I have posted this: Oracle not using indexes if queried in wrong order
What we are experiencing right now is beyond my understanding, and I am asking for explanation.
Query:
SELECT * FROM PREMIUMACCOUNTBOOKING p WHERE PARENTFOREIGNKEY = 499699430 AND CLASSID = 511 ORDER BY BOOKINGNO;
lasts 3 ms.
Explanation plan:
Plan hash value: 3357352015
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 632 | 9 (12)| 00:00:01 |
| 1 | SORT ORDER BY | | 4 | 632 | 9 (12)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| PREMIUMACCOUNTBOOKING | 4 | 632 | 8 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | PREMIUMACCOUNTBOOKI_511_2 | 4 | | 4 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("PARENTFOREIGNKEY"=499699430 AND "CLASSID"=511)
Now, query SELECT * FROM PREMIUMACCOUNTBOOKING WHERE PARENTFOREIGNKEY=499699430 AND (CLASSID=511) ORDER BY BOOKINGNO;
lasts 40 seconds! Repeatedly. No improvement even after several executions.
Explanation plan:
Plan hash value: 3357352015
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 632 | 9 (12)| 00:00:01 |
| 1 | SORT ORDER BY | | 4 | 632 | 9 (12)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| PREMIUMACCOUNTBOOKING | 4 | 632 | 8 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | PREMIUMACCOUNTBOOKI_511_2 | 4 | | 4 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("PARENTFOREIGNKEY"=499699430 AND "CLASSID"=511)
I do not see any difference, except in actual execution time.
What can I do to identify the reason for this behaviour?
Thank you,
GG

Related

Slow inner join in Oracle

I have Oracle database with a main table contain 9 000 000 rows and a second with 19 000 000 rows.
When I do :
SELECT *
FROM main m
INNER JOIN second s ON m.id = s.fk_id AND s.cd = 'E' AND s.line = 1
It's take 45 seconds to get the first part of the result, even with all the index below :
CREATE INDEX IDX_1 ON SECOND (LINE, CD, FK_ID, ID);
CREATE INDEX IDX_1 ON SECOND (LINE, CD);
MAIN (ID) AS PRIMARY KEY
Any idea how to do it faster ? I try some index, rebuild but it's always take 45 seconds
Here is the execution plan :
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8850631 | 2133002071 | 696494 | 00:00:28 |
| * 1 | HASH JOIN | | 8850631 | 2133002071 | 696494 | 00:00:28 |
| * 2 | TABLE ACCESS FULL | SECOND | 8850631 | 646096063 | 143512 | 00:00:06 |
| 3 | TABLE ACCESS FULL | MAIN | 9227624 | 1550240832 | 153363 | 00:00:06 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 1 - access("M"."ID"="S"."FK_ID")
* 2 - filter("S"."CD"='D' AND "S"."LINE"=1)
Thanks
If you want to see the first line quickly you have to enable Oracle to use the NESTED LOOP join.
This will required an index on second with the two columns you constraint in your query and an index on main on the join column id
create index second_idx on second(line,cd);
create index main_idx on main(id);
You'll see an execution plan similar to one below
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 87 | 8178 | 178 (0)| 00:00:03 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 87 | 8178 | 178 (0)| 00:00:03 |
| 3 | TABLE ACCESS BY INDEX ROWID| SECOND | 87 | 2523 | 4 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | SECOND_IDX | 1 | | 3 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | MAIN_IDX | 1 | | 1 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | MAIN | 1 | 65 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("S"."LINE"=1 AND "S"."CD"='E')
5 - access("M"."ID"="S"."FK_ID")
You will access via index all rows in second with requested lineand cd (plan line 4 and 3) and for each such row you'll access via index the main table (lines 5 and 6)
This will provide an instant access to the first few rows and will work fine if there are a low number of rows in second table with the selected line and cd. In other case (when there is a large number of rows with s.cd = 'E' AND s.line = 1 - say 10k+) you will still see the first result rows quickly, but you'll wait ages to see the last row (it will take much more that the 45 seconds to finish the query).
If this is a problem you have to use a HASH JOIN (which you probaly do now).
A hash join typically doesn not use indexes and produced following execution plan
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10182 | 1153K| 908 (1)| 00:00:11 |
|* 1 | HASH JOIN | | 10182 | 1153K| 908 (1)| 00:00:11 |
|* 2 | TABLE ACCESS FULL| SECOND | 10182 | 99K| 520 (2)| 00:00:07 |
| 3 | TABLE ACCESS FULL| MAIN | 90000 | 9316K| 387 (1)| 00:00:05 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("M"."ID"="S"."FK_ID")
2 - filter("S"."LINE"=1 AND "S"."CD"='E')
Summary
To use the nested loops the indexes must be available as described above
The switch between nested loopsand hash join is done by the Oracle database (CBO) - provided that your tables statistics and database configuration are fine.

Oracle SQL indexed query 100% cpu usage

I'm running a relatively simple query
SELECT * FROM confirm_v c
JOIN person p ON c.created_by=p.id
INNER JOIN invoice_confirm ic ON ic.confirm_id=c.id
WHERE c.id = (SELECT id FROM
(SELECT c2.id FROM confirm c2
JOIN invoice_confirm ic2 ON ic2.confirm_id=c2.id
WHERE ic2.invoice_id=11954081
AND c2.previous=0
AND c2.canceled=0
AND c2.confirm_type='INVOICE'
ORDER BY c2.id)
WHERE rownum=1);
which results in 100% cpu usage by the rdb. The confirm_type is a varchar2(50 char), the rest are number(10) if it means anything.
The invoice_confirm and confirm tables are covered by indices and there are no full table scans visible in the execution plan for this query.
This query isn't executed a lot, but accounts for nearly 100% of total cpu usage. Any ideas are appreciated.
EDIT:
The explain plan in text from for the query.
EXPLAIN PLAN FOR ...
SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
Plan hash value: 1705859247
------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 69 | 10 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 69 | 10 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 69 | 10 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 1 | 57 | 7 (0)| 00:00:01 |
| 4 | NESTED LOOPS | | 1 | 30 | 5 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID | CONFIRM | 1 | 24 | 3 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | PK_CONFIRM | 1 | | 2 (0)| 00:00:01 |
|* 7 | COUNT STOPKEY | | | | | |
| 8 | VIEW | | 4 | 52 | 27 (4)| 00:00:01 |
|* 9 | SORT ORDER BY STOPKEY | | 4 | 132 | 27 (4)| 00:00:01 |
| 10 | NESTED LOOPS | | 4 | 132 | 26 (0)| 00:00:01 |
| 11 | NESTED LOOPS | | 11 | 132 | 26 (0)| 00:00:01 |
| 12 | TABLE ACCESS BY INDEX ROWID BATCHED| INVOICE_CONFIRM | 3 | 36 | 4 (0)| 00:00:01 |
|* 13 | INDEX RANGE SCAN | FKI_INVOICE_CONFIRM_INVOICE | 2 | | 3 (0)| 00:00:01 |
|* 14 | INDEX UNIQUE SCAN | PK_CONFIRM | 1 | | 1 (0)| 00:00:01 |
|* 15 | TABLE ACCESS BY INDEX ROWID | CONFIRM | 1 | 21 | 2 (0)| 00:00:01 |
|* 16 | INDEX RANGE SCAN | FKI_INVOICE_CONFIRM_CONFIRM | 1 | 6 | 2 (0)| 00:00:01 |
| 17 | TABLE ACCESS BY INDEX ROWID | PERSON | 1 | 27 | 2 (0)| 00:00:01 |
|* 18 | INDEX UNIQUE SCAN | PK_KASUTAJA | 1 | | 1 (0)| 00:00:01 |
|* 19 | INDEX RANGE SCAN | FKI_INVOICE_CONFIRM_CONFIRM | 1 | | 2 (0)| 00:00:01 |
| 20 | TABLE ACCESS BY INDEX ROWID | INVOICE_CONFIRM | 1 | 12 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("CONFIRM"."ID"= (SELECT "ID" FROM (SELECT "C2"."ID" "ID" FROM "INVOICE_CONFIRM" "IC2","CONFIRM" "C2"
WHERE "IC2"."CONFIRM_ID"="C2"."ID" AND "C2"."CANCELED"=0 AND "C2"."PREVIOUS"=0 AND "C2"."CONFIRM_TYPE"='INVOICE' AND
"IC2"."INVOICE_ID"=11954081 ORDER BY "C2"."ID") "from$_subquery$_006" WHERE ROWNUM=1))
7 - filter(ROWNUM=1)
9 - filter(ROWNUM=1)
13 - access("IC2"."INVOICE_ID"=11954081)
14 - access("IC2"."CONFIRM_ID"="C2"."ID")
15 - filter("C2"."CANCELED"=0 AND "C2"."PREVIOUS"=0 AND "C2"."CONFIRM_TYPE"='INVOICE')
16 - access("IC"."CONFIRM_ID"="CONFIRM"."ID")
18 - access("CONFIRM"."CREATED_BY"="P"."ID")
19 - access("IC"."CONFIRM_ID"="CONFIRM"."ID")
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- 2 Sql Plan Directives used for this statement
Gather optimizer statistics on the relevant tables and investigate why the statistics were missing.
begin
dbms_stats.gather_table_stats(user, 'CONFIRM');
dbms_stats.gather_table_stats(user, 'INVOICE_CONFIRM');
dbms_stats.gather_table_stats(user, 'PERSON');
end;
/
Optimizer statistics are critical for Oracle to achieve good performance. The note dynamic statistics used: dynamic sampling (level=2) implies that there are tables with missing optimizer statistics. That should never happen unless the tables were created within the last day.
Oracle automatically gathers stale and missing statistics. Check if the job is running with this query. If there are no recent rows, ask your DBA to re-enable the task.
select *
from dba_optstat_operations
where operation like '%auto%'
order by start_time desc;
The autotask is good enough for most tables. But if there's a large batch process that updates a lot of rows then the statistics should be manually collected as soon as the job is complete.

Is it better to use sql hint /*+ opt_param('_optimizer_use_feedback' 'false') */ in the select statement

The execution plan:
Plan 1:
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 9 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP_HISTORY | 1 | 190 | 9 (0)| 00:00:01 |
| 2 | INDEX RANGE SCAN | IND_EMP_HISTORY_VALDT | 15 | | 4 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------
Plan 2:
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 281 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP_HISTORY | 3 | 570 | 281 (0)| 00:00:04 |
| 2 | INDEX RANGE SCAN | IND_EMP_HISTORY_EMPNO | 1203 | | 6 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------
I did for first plan1 by disabling the setting parameter "_OPTIMIZER_USE_FEEDBACK" = FALSE at database level, but this is not suitable because it may effect other query.For second paln the sql hint /*+ opt_param('_optimizer_use_feedback' 'false') */ used in select statement.
Could anyone please advice.
Thanks,
Raju

An oracle performance issue on COUNT()

I'm using Oracle 11g, the main table has about 10m records. Here is my query:
SELECT COUNT (*)
FROM CONTACT c INNER JOIN STATUS S ON C.STATUS = S.STATUS
WHERE C.USER = 1 AND S.REQUIRE = 1 AND ROWNUM = 1;
The Cost is 3736, but when I changed it to this form:
SELECT COUNT (*) FROM
(SELECT 1 FROM CONTACT c INNER JOIN STATUS S ON C.STATUS = S.STATUS
WHERE C.USER = 1 AND S.REQUIRE = 1 AND ROWNUM = 1);
The Cost became 5! What's the difference between these 2 queries?
Here are the explain plan for both query:
The first query:
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 3736 (1)| 00:00:45 |
| 1 | SORT AGGREGATE | | 1 | 10 | | |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | NESTED LOOPS | | 4627 | 46270 | 3736 (1)| 00:00:45 |
| 4 | TABLE ACCESS BY INDEX ROWID| CONTACT | 6610 | 33050 | 3736 (1)| 00:00:45 |
|* 5 | INDEX RANGE SCAN | IX_CONTACT_USR | 6610 | | 20 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IX_CONTACT_STATUS | 1 | 5 | 0 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(ROWNUM=1)
5 - access("C"."USER"=1)
6 - access("C"."STATUS"="S"."STATUS" AND "S"."REQUIRE"=1)
The second query:
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 5 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | | |
| 2 | VIEW | | 1 | | 5 (0)| 00:00:01 |
|* 3 | COUNT STOPKEY | | | | | |
| 4 | NESTED LOOPS | | 2 | 20 | 5 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| CONTACT | 3 | 15 | 5 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IX_CONTACT_USR | 6610 | | 3 (0)| 00:00:01 |
|* 7 | INDEX RANGE SCAN | IX_CONTACT_STATUS | 1 | 5 | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(ROWNUM=1)
6 - access("C"."USER"=1)
7 - access("C"."STATUS"="S"."STATUS" AND "S"."REQUIRE"=1)
I executed 2 queries, the first one sometimes cost 45s+ (e.g. first run or change the user id), otherwise it will cost <1s. I totally don't know why it's such different, maybe db cache?
When I executed the second query, I can always get result in 1s. So I think the second one is better, but I don't the reason why it improves a lot.
You can see where the difference comes in by comparing the line in the execution plans that access the CONTACT table (looks at the rows column, the first one).
First:
| 4 | TABLE ACCESS BY INDEX ROWID| CONTACT | 6610 | 33050 | 3736 (1)| 00:00:45 |
Second:
| 5 | TABLE ACCESS BY INDEX ROWID| CONTACT | 3 | 15 | 5 (0)| 00:00:01 |
In the first example, the ROWNUM = 1 predicate isn't applied until after the CONTACT table has been accessed, so you're getting 6610 rows returned from this table. Whereas in your second query optimizer has only returned 3. This is many orders of magnitude less, which is why you're seeing the second query complete quicker.
As to why the second execution of the "slow" query is "fast", you're thinking is correct - the data has been loaded from disk into the buffer cache so access is much quicker.
Most likely that's just estimation difference and they will have same execution statistics. Trace both + tkprof to get real data.
Also if you want some more details behind optimizer logic - do hard parse with event 10053.
Cost is not only the factor for the queries, some times it depends on the server also, which u r showing is it a CPU cost or I/O Cost, some times cost my vary, because of the Column Cardinality, Conditions of the query. if u wanna see the much clarification on the queries, get the explain plan or TKPROOF, so that u 'll get to know , it's going for full table scan or which index is picking up and execution time.

SQL tuning issue

I have a query:
select count(1) CNT
from file_load_params a
where a.doc_type = (select b.doc_type
from file_load_header b
where b.indicator = 'XELFASI')
order by a.line_no
Which explain plan is:
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
|* 2 | TABLE ACCESS FULL | FILE_LOAD_PARAMS | 15 | 105 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| FILE_LOAD_HEADER | 1 | 12 | 1 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | FILE_LOAD_HEADER_UK | 1 | | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
I thought that I could optimize this query and write this one:
select count(1) CNT
from file_load_params a,file_load_header b
where b.indicator = 'XELFASI'
and a.doc_type = b.doc_type
order by a.line_no
Its explain plan is:
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
| 2 | NESTED LOOPS | | 15 | 285 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| FILE_LOAD_HEADER | 1 | 12 | 1 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | FILE_LOAD_HEADER_UK | 1 | | 0 (0)| 00:00:01 |
|* 5 | TABLE ACCESS FULL | FILE_LOAD_PARAMS | 15 | 105 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Is it good? I think not,but I expected better result...Do you have any idea?
From the explain plans, these appear to be tiny tables and the cost of the query is negligible. How long do they take to run and how quickly do you need them to run ?
But remove the ORDER BY. Since you are selecting a single row COUNT aggregate it is pointless.
One of the possible optimizations i see from your explain plan is
TABLE ACCESS FULL | FILE_LOAD_PARAMS
This seems to indicate that table file_load_params possibly does not have any index on doc_type
If that is the case, can you add an index for doc_type. If you already have indexes, can you post your table schema for file_load_params
The result is not the same for the two queries. The IN operator automatically also applies a DISTINCT to the inner query. And in this case it is probably not a key you are joining on (if it is, then make it an unique key), so it cannot be optimized away.
As for optimizing the query, then do as InSane says, add an index on Doc_Type in FILE_LOAD_PARAMS