Sql 优化器未使用索引

Sql 优化器未使用索引,sql,oracle,performance,indexing,Sql,Oracle,Performance,Indexing,我有一个查询,由于对一个表进行了完全扫描,所以执行得非常糟糕。我已经检查了统计数据并重建了索引,但它不起作用 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',

我有一个查询,由于对一个表进行了完全扫描,所以执行得非常糟糕。我已经检查了统计数据并重建了索引,但它不起作用

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

在开始时,WHERE子句中的条件的一部分必须分解(或“反编译”-或“重新生成”)为更简单的形式,而不使用
decode
函数,查询优化器可以理解该函数:

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

要根据索引中存储的值查找表中的行,Oracle使用名为索引扫描的访问方法,有关详细信息,请参阅此链接:


最常见的访问方法之一是索引范围扫描请参见此处:


文件(在后一链接中)指出:

优化器在找到一个或多个前导项时使用范围扫描 条件中指定的索引列,例如:

col1=:b1

col1<:b1

col1>:b1

以及前面条件的组合,用于 索引

col1(如“ASD%”通配符搜索)不应处于领先位置 否则,条件col1(如“%ASD”)不会导致 范围扫描

上述情况意味着优化器能够使用索引仅为包含基本比较运算符的查询条件查找行:
=<>=LIKE
,用于将简单值与普通列名进行比较

文档没有明确说明的是一个事实,即当某个函数在条件中使用时,以
函数(列名称)
函数(包含列名称的表达式)
的形式使用时,就不能使用索引范围扫描
在这种情况下,查询优化器必须为表中的每一行单独计算此表达式,因此必须读取所有行(执行完整表扫描)


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

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

如果您在WHERE子句中看到某个函数,那么这表明您是 闯红灯
立即停止并三思而后行 此函数会影响查询优化器和数据库的性能 查询,并尝试将条件重写为优化器允许的表单 他能够理解


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

AND (
    :P55_DIRECT = 'ALL'
    OR
    trim(upper(:P55_DIRECT)) = trimm(upper(NA_ORG_OWNER_EMAIL))
)
STOP-仍有两个功能
trim
upper
应用于名为NA_ORG\u OWNER\u EMAIL的列。我们需要考虑它们如何影响查询优化器。

我假设您已在单个列上创建了一个普通索引:
在GCR\u项目(NA\u ORG\u OWNER\u EMAIL)上创建索引somename

如果是,则索引仅包含NA\u ORG\u OWNER\u EMAIL的普通值。
但是查询正在尝试查找
trimm(upper(NA_ORG\u OWNER\u EMAIL))
值,这些值未存储在索引中,因此在这种情况下无法使用此索引。

这种情况需要基于函数的索引:


在GCR\u项目上创建索引somename(trim(上部(NA\u组织\u所有者\u电子邮件))


不幸的是,即使是基于函数的索引也无济于事,因为查询中的条件太一般-如果值为:P55_DIRECT=ALL,则查询必须检索表中的所有行(执行完整表扫描),否则必须使用索引搜索其中的值。

这是因为查询优化器在第一次执行期间只计划(将其视为“编译”)一次查询。然后,该计划存储在缓存中,并用于执行所有后续执行的查询。参数的值事先不知道,因此计划必须考虑每个可能的情况,因此总是执行全表扫描。 12c中有一个新功能“自适应查询优化”:

其中,查询优化器在每次运行时分析查询的每个参数,并且能够检测到计划对于某些运行时参数不是最优的,并根据实际参数的值选择更好的“子计划”。。。但您必须使用12c,并另外支付Enterprise Edition的费用,因为只有此版本包含该功能。在这种情况下,适应性计划是否有效仍不确定。

无需支付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

您可以发布执行计划和表上的索引吗?如果不知道表上有哪些索引,也不查看执行计划,就无法猜测发生了什么。请编辑您的问题并包括:A)两个表的DDL,B)两个表上的所有索引,C)执行计划,以及D)每个表中的行数。谢谢。
gcr\u items
仅包含7386行-我怀疑索引访问是否真的能提高性能,因为显然您正在从该表中选择所有行
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