我有一个查询,由于对表的完全扫描而表现非常糟糕。我检查了统计信息重建索引但是它不起作用。
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))))
答案 0 :(得分:0)
在开始时,WHERE子句中的一部分条件必须在不使用decode
的情况下分解为(或#34;反编译" - 或" reengeenered")为更简单的形式函数,查询优化器可以理解表单:
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使用名为 索引扫描 的访问方法,有关详细信息,请参阅此链接:
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的前导条件的组合 索引
col1喜欢' ASD%'外卡搜索不应该处于领先地位 否则条件col1喜欢'%ASD'不会导致 范围扫描。
以上意味着优化器能够使用索引仅查找包含基本比较运算符的查询条件的行:= < > <= >= LIKE
,用于将简单值与普通列名称进行比较。
文件没有明确说明 - 并且你需要推断它在行之间的读数 - 这是一个事实,当某个函数在条件中使用时,形式为{{1} }或function( column_name )
,然后无法使用索引范围扫描。
在这种情况下,查询优化器必须为表中的每一行单独计算此表达式,因此必须读取所有行(执行全表扫描)。
简短的结论和经验法则:
WHERE子句中的函数可能会阻止优化程序使用 索引强>
如果你在WHERE子句中的某个地方看到某个函数,那么它就是你的标志 运行红灯
立即停止并三思考如何 这个函数会影响查询优化器和你的性能 查询,并尝试将条件重写为优化器的表单 能够理解。
现在看看我们的重写条件:
function( expression_involving_column_names )
和停止 - 仍然有两个函数AND (
:P55_DIRECT = 'ALL'
OR
trim(upper(:P55_DIRECT)) = trimm(upper(NA_ORG_OWNER_EMAIL))
)
和trim
应用于名为NA_ORG_OWNER_EMAIL的列。我们需要考虑它们如何影响查询优化器。
我假设您已在单个列上创建了普通索引:
upper
。
如果是,则索引仅包含NA_ORG_OWNER_EMAIL的简单值。
但查询正在尝试查找未存储在索引中的CREATE INDEX somename ON GCR_ITEMS( NA_ORG_OWNER_EMAIL )
值,因此在这种情况下不能使用此索引。
此条件需要基于函数的索引:
https://docs.oracle.com/cd/E11882_01/appdev.112/e41502/adfns_indexes.htm#ADFNS00505
trimm(upper(NA_ORG_OWNER_EMAIL))
不幸的是,即使基于函数的索引仍然无济于事,因为查询中的条件太笼统 - 如果值为:P55_DIRECT = ALL,则查询必须从表中检索所有行(执行全表扫描),否则必须使用索引来搜索其中的值。
这是因为查询优化器在其首次执行期间只计划了一次查询(将其视为&#34;编译&#34;)。然后,计划存储在缓存中,用于执行所有进一步执行的查询。事先不知道参数的值,因此计划必须考虑每种可能的情况,因此将始终执行全表扫描。
在12c中有一个新功能&#34;自适应查询优化&#34;:
https://docs.oracle.com/database/121/TGSQL/tgsql_optcncpt.htm#TGSQL94982
查询优化器在每次运行时分析查询的每个参数,并且能够检测到计划对于某些运行时参数不是最佳的,并选择更好的&#34;子计划&#34;取决于实际参数的值......但您必须使用12c,并且还要支付Enterprise Edition,因为只有此版本包含该功能。并且仍然不确定自适应计划是否适用于这种情况。
如果不支付12c EE,你可以做的就是将这个一般查询划分为两个独立的变体,一个用于以下情况:P55_DIRECT = ALL,另一个用于其余情况,并在客户端(您的应用程序)中运行适当的变体,具体取决于关于此参数的值。
以下版本:P55_DIRECT = ALL,将执行全表扫描
CREATE INDEX somename ON GCR_ITEMS( trim( upper( NA_ORG_OWNER_EMAIL )))
以及其他案例的版本,将使用基于函数的索引:
where gcr_deals.GCR_DEALS_ID=gcr_items.GCR_DEALS_ID
and
gcr_deals.bu_id=:P0_BU_ID
order by 1