繁体   English   中英

优化器未使用索引

[英]Index is not being used by optimizer

我有一个查询,由于对表进行全面扫描而导致性能非常差。我检查了统计信息以重建索引,但它不起作用。

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))))

首先,必须在不使用decode功能的情况下将WHERE子句中的条件的一部分分解(或“反编译”或“重新复仇”)成更简单的形式,查询优化器可以理解这种形式:

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

变成:

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

为了根据索引中存储的值在表中查找行,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#i45075

该文档说(在后一个链接中):

当发现条件中指定的索引的一个或多个前导列时,优化器将使用范围扫描,例如:

col1 =:b1

col1 <:b1

col1>:b1

索引中前导列的前述条件的AND组合

像'ASD%'这样的col1通配符搜索不应处于领先位置,否则像'%ASD'这样的条件col1不会导致范围扫描。

上面的意思是说,优化程序只能使用索引来查找包含基本比较运算符的查询条件的行: = < > <= >= LIKE ,用于将简单值与纯列名称进行比较

文档没有明确说出-您需要推断出它们在行与行之间的读数-这是一个事实,即在条件中使用某些函数时,形式为function( column_name )function( expression_involving_column_names ) ,然后是索引范围无法使用扫描
在这种情况下,查询优化器必须为表中的每一行分别评估该表达式,因此必须读取所有行(执行全表扫描)。


一个简短的结论和经验法则:

WHERE子句中的函数可能会阻止优化器使用索引

如果您在WHERE子句中看到某个功能,则表明您正在运行红灯
立即停止并思考此函数如何影响查询优化器和查询性能的三倍,然后尝试将条件重写为优化器能够理解的形式。


现在看一下我们重写的条件:

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

STOP -仍有两个函数trimupper施加到名为NA_ORG_OWNER_EMAIL柱。 我们需要考虑它们如何影响查询优化器。

我假设您在单列上创建了一个普通索引:
CREATE INDEX somename ON GCR_ITEMS( NA_ORG_OWNER_EMAIL )
如果是,则索引仅包含NA_ORG_OWNER_EMAIL的纯值。
但是查询正在尝试查找trimm(upper(NA_ORG_OWNER_EMAIL))值,这些值未存储在索引中,因此在这种情况下不能使用此索引。

这种情况需要基于函数的索引
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 )))


不幸的是,即使基于函数的索引仍然无法解决问题,因为查询中的条件过于笼统-如果值:P55_DIRECT = ALL,则查询必须从表中检索所有行(执行全表扫描),否则必须使用索引以在其中搜索值。

这是因为查询优化器在第一次执行时仅计划了一次查询(将其视为“已编译”)。 然后,该计划将存储在缓存中,并用于对所有进一步执行执行查询。 该参数的值事先未知,因此该计划必须考虑每种可能的情况,因此将始终执行全表扫描。
在12c中,有一个新功能“自适应查询优化”:
https://docs.oracle.com/database/121/TGSQL/tgsql_optcncpt.htm#TGSQL94982
查询优化器在其中分析每次运行时查询的每个参数,并能够检测出该计划对于某些运行时参数不是最佳选择,并根据实际参数的值选择更好的“子计划” ...但是您必须使用12c ,并另外为企业版付费,因为只有该版本包含该功能。 在这种情况下,仍然不确定自适应计划是否会奏效。

您无需付费即可使用12c EE的方法是将此常规查询分为两个单独的变体,一个用于:P55_DIRECT = ALL的情况,另一个用于其余情况,然后根据需要在客户端(您的应用程序)中运行适当的变体根据此参数的值。

: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

以及用于其他情况的版本,该版本将使用基于函数的索引:

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