耗时较长的SQL
select msn.serial_number, msi.description from mtl_system_items_tl msi , mtl_serial_numbers msn where ( UPPER(SERIAL_NUMBER) LIKE :1 AND (SERIAL_NUMBER LIKE :2 OR SERIAL_NUMBER LIKE :3 OR SERIAL_NUMBER LIKE :4 OR SERIAL_NUMBER LIKE :5)) AND ( msn.current_organization_id = msi.organization_id and msn.inventory_item_id = msi.inventory_item_id and msn.inventory_item_id = nvl( :6, msn.inventory_item_id) /* and msn.current_organization_id = :parameter.org_id */ and msi.language = userenv('LANG') ) order by serial_number call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 1 27.61 375.71 426621 429306 0 1 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 3 27.61 375.72 426621 429306 0 1 Misses in library cache during parse: 1 Misses in library cache during execute: 1 Optimizer mode: ALL_ROWS Parsing user id: 44 (APPS) Rows Row Source Operation ------- --------------------------------------------------- 1 SORT ORDER BY (cr=429306 pr=426621 pw=0 time=375714574 us) 1 CONCATENATION (cr=429306 pr=426621 pw=0 time=375714503 us) 1 FILTER (cr=429306 pr=426621 pw=0 time=375714488 us) 1 NESTED LOOPS (cr=429306 pr=426621 pw=0 time=375714482 us) 1 TABLE ACCESS FULL MTL_SERIAL_NUMBERS (cr=429302 pr=426620 pw=0 time=375707835 us) 1 TABLE ACCESS BY INDEX ROWID MTL_SYSTEM_ITEMS_TL (cr=4 pr=1 pw=0 time=6639 us) 1 INDEX UNIQUE SCAN MTL_SYSTEM_ITEMS_TL_U1 (cr=3 pr=0 pw=0 time=29 us)(object id 118408) 0 FILTER (cr=0 pr=0 pw=0 time=2 us) 0 NESTED LOOPS (cr=0 pr=0 pw=0 time=0 us) 0 TABLE ACCESS BY INDEX ROWID MTL_SERIAL_NUMBERS (cr=0 pr=0 pw=0 time=0 us) 0 INDEX RANGE SCAN MTL_SERIAL_NUMBERS_U1 (cr=0 pr=0 pw=0 time=0 us)(object id 213230) 0 TABLE ACCESS BY INDEX ROWID MTL_SYSTEM_ITEMS_TL (cr=0 pr=0 pw=0 time=0 us) 0 INDEX UNIQUE SCAN MTL_SYSTEM_ITEMS_TL_U1 (cr=0 pr=0 pw=0 time=0 us)(object id 118408) Rows Execution Plan ------- --------------------------------------------------- 0 SELECT STATEMENT MODE: ALL_ROWS 1 SORT (ORDER BY) 1 CONCATENATION 1 FILTER 1 NESTED LOOPS 1 TABLE ACCESS MODE: ANALYZED (FULL) OF 'MTL_SERIAL_NUMBERS' (TABLE) 1 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID) OF 'MTL_SYSTEM_ITEMS_TL' (TABLE) 1 INDEX MODE: ANALYZED (UNIQUE SCAN) OF 'MTL_SYSTEM_ITEMS_TL_U1' (INDEX (UNIQUE)) 0 FILTER 0 NESTED LOOPS 0 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID) OF 'MTL_SERIAL_NUMBERS' (TABLE) 0 INDEX MODE: ANALYZED (RANGE SCAN) OF 'MTL_SERIAL_NUMBERS_U1' (INDEX (UNIQUE)) 0 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID) OF 'MTL_SYSTEM_ITEMS_TL' (TABLE) 0 INDEX MODE: ANALYZED (UNIQUE SCAN) OF 'MTL_SYSTEM_ITEMS_TL_U1' (INDEX (UNIQUE)) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ SQL*Net message to client 1 0.00 0.00 db file sequential read 56 0.02 0.55 db file scattered read 26778 0.49 355.63 latch free 3 0.00 0.00 SQL*Net message from client 1 0.00 0.00
mtl_system_items_b:650647
mtl_serial_numbers:10947015
分析:
where子句中大量使用OR关联词,
SERIAL_NUMBER LIKE :2 OR SERIAL_NUMBER LIKE :3 OR SERIAL_NUMBER LIKE :4 OR SERIAL_NUMBER LIKE :5
OR关联词在CBO解析时,会变形(EXPAND)为UNION ALL的关联语句。
如:select * from t where P or Q 会展开成类似下面的语句:
select * from t where P
union all
select * from t where Q and NOT(P)
这样的变形在有的时候会引起性能问题。
解决方法:
引入/*+ no_expand */ hint到sql中,no_expand 的含义是不执行OR-expansion。
Tom Kyte:
The NO_EXPAND hint prevents the cost-based optimizer from considering OR-expansion for queries having OR conditions or IN-lists in the WHERE clause. Usually, the optimizer considers using OR expansion and uses this method if it decides the cost is lower than not using it. NO_EXPAND is the opposite of USE_CONCAT. Use the no-expand when it goes faster. The optimizer might have many more permutations to consider with a OR-expansion then without -- leading to a longer parse time and perhaps a less optimal plan (if it gives up soon enough).
Doc:http://docs.oracle.com/cd/E11882_01/server.112/e10592/sql_elements006.htm#SQLRF50502
NO_EXPAND Hint
(See "Specifying a Query Block in a Hint")
The NO_EXPAND
hint instructs the optimizer not to consider OR
-expansion for queries having OR
conditions or IN
-lists in the WHERE
clause. Usually, the optimizer considers using OR
expansion and uses this method if it decides that the cost is lower than not using it. For example:
SELECT /*+ NO_EXPAND */ * FROM employees e, departments d WHERE e.manager_id = 108 OR d.department_id = 110;
Comments from Performance Expert
There are several things to consider here. First (and you might already have known this), hints never change the outcome of a sql statement. By that, I mean that you can have a NO_EXPAND, LEADING, ORDERED, or whatever hints, and the query should still return the same rows. It only affects the join order and execution, not the result. So from that perspective, having the NO_EXPAND hint or not will not change the results returned from the query.
In this case, the NO_EXPAND hint is preventing the optimizer from splitting the sql into two or more UNION'd clauses (represented by CONCATENATE action in the plan). In this case, the where clause conditions that cause the CONCATENATE are the serial_number comparisons (LIKE OR LIKE OR LIKE). There are really two possible scenarios here. In the first, we are doing a full scan on MTL_SERIAL_NUMBERS to filter for the series of OR conditions. In the second, we do an index range scan on MTL_SERIAL_NUMBERS_U1. The full scan of MTL_SERIAL_NUMBERS appears to be the problem.
My original thought on this was that you should probably create a second record group (where inventory_item_id is not passed in) and then dynamically assign the proper record group based on whether :6 was populated or not. But upon further study, I think that the NO_EXPAND hint probably just results in more widespread use of the moderately effective N12 index. Since N12 uses both the UPPER(SERIAL_NUMBER) and INVENTORY_ITEM_ID, it would work in both scenarios (:6 NULL or NOT NULL). You can use the NO_EXPAND hint generally in your form record group and it should work adequately in those two scenarios. In essence, the NO_EXPAND hint is de-emphasizing the importance of :2, :3, :4, and :5. If those binds turn out to be critical to the execution, we'll end up having another conversation on this subject then.
修复后:
select /*+ no_expand*/ msn.serial_number, msi.description from mtl_system_items_tlmsi , mtl_serial_numbers msn where (UPPER(SERIAL_NUMBER) LIKE :1 AND (SERIAL_NUMBER LIKE :2 OR SERIAL_NUMBERLIKE :3 OR SERIAL_NUMBER LIKE :4 OR SERIAL_NUMBER LIKE :5)) AND( msn.current_organization_id = msi.organization_id andmsn.inventory_item_id =msi.inventory_item_id and msn.inventory_item_id = nvl( :6, msn.inventory_item_id)/* and msn.current_organization_id = :parameter.org_id*/ and msi.language = userenv('LANG') ) order by serial_number call count cpu elapsed disk query current rows ------------- -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.01 0.00 0 4 0 0 Fetch 1 0.00 0.08 4 9 0 1 ------------- -------- ---------- ---------- ---------- ---------- ---------- total 3 0.01 0.08 4 13 0 1 Misses in librarycache during parse: 1 Misses in librarycache during execute: 1 Optimizer mode:ALL_ROWS Parsing user id:44 (APPS) Rows Execution Plan ------- --------------------------------------------------- 0 SELECT STATEMENT MODE: ALL_ROWS 0 SORT (ORDER BY) 0 NESTED LOOPS 0 TABLE ACCESS MODE: ANALYZED (BY INDEXROWID) OF 'MTL_SERIAL_NUMBERS'(TABLE) 0 INDEX MODE: ANALYZED (RANGE SCAN)OF 'MTL_SERIAL_NUMBERS_N12'(INDEX) 0 TABLE ACCESS MODE: ANALYZED (BY INDEXROWID) OF 'MTL_SYSTEM_ITEMS_TL'(TABLE) 0 INDEX MODE: ANALYZED (UNIQUE SCAN)OF 'MTL_SYSTEM_ITEMS_TL_U1'(INDEX (UNIQUE)) Elapsed timesinclude waiting on following events: Eventwaitedon Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ SQL*Netmessage toclient 1 0.00 0.00 db filesequentialread 4 0.05 0.08 SQL*Netmessage fromclient 1 0.01 0.01 ********************************************************************************