简体   繁体   English

优化器未使用索引

[英]Index is not being used by optimizer

I have a query which is performing very badly due to full scan of a table.I have checked the statistics rebuild the indexes but its not working. 我有一个查询,由于对表进行全面扫描而导致性能非常差。我检查了统计信息以重建索引,但它不起作用。

SQL Statement: SQL语句:

select distinct NA_DIR_EMAIL d, NA_DIR_EMAIL r
from gcr_items , gcr_deals
where gcr_deals.GCR_DEALS_ID=gcr_items.GCR_DEALS_ID 
and 
gcr_deals.bu_id=:P0_BU_ID
and 
decode(:P55_DIRECT,'ALL','Y',trim(upper(NA_ORG_OWNER_EMAIL)))=
decode(:P55_DIRECT,'ALL','Y',trim(upper(:P55_DIRECT))) 
order by 1

Execution Plan :
Plan hash value: 3180018891

-------------------------------------------------------------------------
| Id  | Operation                 | Name             | Rows  | Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |                  |     8 | 00:11:42 |
|   1 |  SORT ORDER BY            |                  |     8 | 00:11:42 |
|   2 |   HASH UNIQUE             |                  |     8 | 00:11:42 |
|*  3 |    HASH JOIN              |                  |  7385 | 00:11:42 |
|*  4 |     VIEW                  | index$_join$_002 | 10462 | 00:00:05 |
|*  5 |      HASH JOIN            |                  |       |          |
|*  6 |       INDEX RANGE SCAN    | GCR_DEALS_IDX12  | 10462 | 00:00:01 |
|   7 |       INDEX FAST FULL SCAN| GCR_DEALS_IDX1   | 10462 | 00:00:06 |
|*  8 |     TABLE ACCESS FULL     | GCR_ITEMS        |  7386 | 00:11:37 |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("GCR_DEALS"."GCR_DEALS_ID"="GCR_ITEMS"."GCR_DEALS_ID")
   4 - filter("GCR_DEALS"."BU_ID"=TO_NUMBER(:P0_BU_ID))
   5 - access(ROWID=ROWID)
   6 - access("GCR_DEALS"."BU_ID"=TO_NUMBER(:P0_BU_ID))
   8 - filter(DECODE(:P55_DIRECT,'ALL','Y',TRIM(UPPER("NA_ORG_OWNER_EMAI
              L")))=DECODE(:P55_DIRECT,'ALL','Y',TRIM(UPPER(:P55_DIRECT))))

In the beginning a part of the condition in the WHERE clause must be decomposed (or "decompiled" - or "reengeenered") into a simpler form without using decode function, which a form can be understandable by the query optimizer: 首先,必须在不使用decode功能的情况下将WHERE子句中的条件的一部分分解(或“反编译”或“重新复仇”)成更简单的形式,查询优化器可以理解这种形式:

AND
decode(:P55_DIRECT,'ALL','Y',trim(upper(NA_ORG_OWNER_EMAIL)))=
decode(:P55_DIRECT,'ALL','Y',trim(upper(:P55_DIRECT))) 

into: 变成:

AND (
    :P55_DIRECT = 'ALL'
    OR
    trim(upper(:P55_DIRECT)) = trimm(upper(NA_ORG_OWNER_EMAIL))
)

To find rows in the table based on values stored in the index, Oracle uses an access method named Index scan , see this link for details: 为了根据索引中存储的值在表中查找行,Oracle使用一种名为Index scan的访问方法,有关详细信息,请参见此链接:
https://docs.oracle.com/cd/B19306_01/server.102/b14211/optimops.htm#i52300 https://docs.oracle.com/cd/B19306_01/server.102/b14211/optimops.htm#i52300

One of the most common access method is Index Range Scan see here: 最常见的访问方法之一是“ 索引范围扫描”,请参见此处:
https://docs.oracle.com/cd/B19306_01/server.102/b14211/optimops.htm#i45075 https://docs.oracle.com/cd/B19306_01/server.102/b14211/optimops.htm#i45075

The documentation says (in the latter link) that: 该文档说(在后一个链接中):

The optimizer uses a range scan when it finds one or more leading columns of an index specified in conditions, such as the following: 当发现条件中指定的索引的一个或多个前导列时,优化器将使用范围扫描,例如:

col1 = :b1 col1 =:b1

col1 < :b1 col1 <:b1

col1 > :b1 col1>:b1

AND combination of the preceding conditions for leading columns in the index 索引中前导列的前述条件的AND组合

col1 like 'ASD%' wild-card searches should not be in a leading position otherwise the condition col1 like '%ASD' does not result in a range scan. 像'ASD%'这样的col1通配符搜索不应处于领先位置,否则像'%ASD'这样的条件col1不会导致范围扫描。

The above means that the optimizer is able to use the index to find rows only for query conditions that contain basic comparision operators: = < > <= >= LIKE which are used to comparing simple values with plain column names . 上面的意思是说,优化程序只能使用索引来查找包含基本比较运算符的查询条件的行: = < > <= >= LIKE ,用于将简单值与纯列名称进行比较

What the documentation doesn't clearly say - and you need to deduce it reading between the lines - is a fact that when some function is used in the condition, in a form function( column_name ) or function( expression_involving_column_names ) , then the index range scan cannot be used . 文档没有明确说出-您需要推断出它们在行与行之间的读数-这是一个事实,即在条件中使用某些函数时,形式为function( column_name )function( expression_involving_column_names ) ,然后是索引范围无法使用扫描
In this case the query optimizer must evaluate this expression individually for each row in the table, thus must read all rows (perform a full table scan). 在这种情况下,查询优化器必须为表中的每一行分别评估该表达式,因此必须读取所有行(执行全表扫描)。


A short conclusion and a rule of thumb: 一个简短的结论和经验法则:

Functions in the WHERE clause can prevent the optimizer from using indexes WHERE子句中的函数可能会阻止优化器使用索引

If you see some function somewhere in the WHERE clause, then it is a sign that you are running the red light 如果您在WHERE子句中看到某个功能,则表明您正在运行红灯
STOP immediately and think three times how this function impact the query optimizer and the performance of your query, and try to rewrite the condition to a form that the optimizer is able to understand. 立即停止并思考此函数如何影响查询优化器和查询性能的三倍,然后尝试将条件重写为优化器能够理解的形式。


Now take a look at our rewritten condition: 现在看一下我们重写的条件:

AND (
    :P55_DIRECT = 'ALL'
    OR
    trim(upper(:P55_DIRECT)) = trimm(upper(NA_ORG_OWNER_EMAIL))
)

and STOP - there are still two functions trim and upper applied to a column named NA_ORG_OWNER_EMAIL. STOP -仍有两个函数trimupper施加到名为NA_ORG_OWNER_EMAIL柱。 We need to think how they can impact the query optimizer. 我们需要考虑它们如何影响查询优化器。

I assume that you have created a plain index on a single column: 我假设您在单列上创建了一个普通索引:
CREATE INDEX somename ON GCR_ITEMS( NA_ORG_OWNER_EMAIL ) . CREATE INDEX somename ON GCR_ITEMS( NA_ORG_OWNER_EMAIL )
If yes, then the index contains only plain values of NA_ORG_OWNER_EMAIL. 如果是,则索引仅包含NA_ORG_OWNER_EMAIL的纯值。
But the query is trying to find trimm(upper(NA_ORG_OWNER_EMAIL)) values, which are not stored in the index, so this index cannot be used in this case. 但是查询正在尝试查找trimm(upper(NA_ORG_OWNER_EMAIL))值,这些值未存储在索引中,因此在这种情况下不能使用此索引。

This condition requires a function based index : 这种情况需要基于函数的索引
https://docs.oracle.com/cd/E11882_01/appdev.112/e41502/adfns_indexes.htm#ADFNS00505 https://docs.oracle.com/cd/E11882_01/appdev.112/e41502/adfns_indexes.htm#ADFNS00505

CREATE INDEX somename ON GCR_ITEMS( trim( upper( NA_ORG_OWNER_EMAIL )))


Unfortunately even the function based index will still not help, because the condition in the query is too general - if a value of :P55_DIRECT = ALL the query must retrieve all rows from the table (perform a full table scan), otherwise must use the index to search value within it. 不幸的是,即使基于函数的索引仍然无法解决问题,因为查询中的条件过于笼统-如果值:P55_DIRECT = ALL,则查询必须从表中检索所有行(执行全表扫描),否则必须使用索引以在其中搜索值。

This is because the query is planned (think of it as "compiled") by the query optimizer only once, during it's first execution. 这是因为查询优化器在第一次执行时仅计划了一次查询(将其视为“已编译”)。 Then the plan is stored in the cache and used to execute the query for all further executions. 然后,该计划将存储在缓存中,并用于对所有进一步执行执行查询。 A value of the parameter is not know in advance, so the plan must consider each possible cases, thus will always perform a full table scan. 该参数的值事先未知,因此该计划必须考虑每种可能的情况,因此将始终执行全表扫描。
In 12c there is a new feature "Adaptive query optimalization": 在12c中,有一个新功能“自适应查询优化”:
https://docs.oracle.com/database/121/TGSQL/tgsql_optcncpt.htm#TGSQL94982 https://docs.oracle.com/database/121/TGSQL/tgsql_optcncpt.htm#TGSQL94982
where the query optimizer analyses each parameters of the query on each runs, and is able to detect that the plan is not optimal for some runtime parameters, and choose a better "subplans" depending on actual parameter's value ... but you must use 12c, and additionally pay for Enterprise Edition, because only this edition includes that feature. 查询优化器在其中分析每次运行时查询的每个参数,并能够检测出该计划对于某些运行时参数不是最佳选择,并根据实际参数的值选择更好的“子计划” ...但是您必须使用12c ,并另外为企业版付费,因为只有该版本包含该功能。 And it's still not certain if the adaptive plan will work in this case or not. 在这种情况下,仍然不确定自适应计划是否会奏效。

What you can do without paying for 12c EE is to DIVIDE this general query into two separate variants, one for a case where :P55_DIRECT = ALL, and the other for remaining cases, and run an appropriate variant in the client (your application) depending on the value of this parameter. 您无需付费即可使用12c EE的方法是将此常规查询分为两个单独的变体,一个用于:P55_DIRECT = ALL的情况,另一个用于其余情况,然后根据需要在客户端(您的应用程序)中运行适当的变体根据此参数的值。

A version for :P55_DIRECT = ALL, that will perform a full table scan :P55_DIRECT = ALL的版本,将执行全表扫描

where gcr_deals.GCR_DEALS_ID=gcr_items.GCR_DEALS_ID 
and 
gcr_deals.bu_id=:P0_BU_ID
order by 1

and a version for other cases, that will use the function based index: 以及用于其他情况的版本,该版本将使用基于函数的索引:

where gcr_deals.GCR_DEALS_ID=gcr_items.GCR_DEALS_ID 
and 
gcr_deals.bu_id=:P0_BU_ID
and 
 trim(upper(:P55_DIRECT)) = trimm(upper(NA_ORG_OWNER_EMAIL))
order by 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM