我遇到一段代码问题,当wrk.cre_surr_id是主键时,我无法理解为什么以下查询在工作表上进行全表扫描。两个表上的统计数据都是最新的,是两个表的索引。
TABLE INDEXES
WORKS
INDEX NAME UNIQUE LOGGING COLUMN NAME ORDER
WRK_I1 N NO LOGICALLY_DELETED_Y Asc
WRK_ICE_WRK_KEY N YES ICE_WRK_KEY Asc
WRK_PK Y NO CRE_SURR_ID Asc
WRK_TUNECODE_UK Y NO TUNECODE Asc
TLE_TITLE_TOKENS
INDEX NAME UNIQUE LOGGING COLUMN NAME ORDER
TTT_I1 N YES TOKEN_TYPE, Asc
SEARCH_TOKEN,
DN_WRK_CRE_SURR_ID
TTT_TLE_FK_1 N YES TLE_SURR_ID
下面的问题查询。它的成本为245,876,看起来很高,它正在对WORKS表进行FULL TABLE扫描,表中有21,938,384行。它正在执行TLE_TITLE_TOKENS表的INDEX RANGE SCAN,其中包含19,923,002行。在解释计划上也是一个INLIST ITERATOR,我不知道这意味着什么,但我认为它与#34;(' E',& #39; N')"在我的SQL查询中。
SELECT wrk.cre_surr_id
FROM works wrk,
tle_title_tokens ttt
WHERE ttt.dn_wrk_cre_surr_id = wrk.cre_surr_id
AND wrk.logically_deleted_y IS NULL
AND ttt.token_type in ('E','N')
AND ttt.search_token LIKE 'BELIEVE'||'%'
当我打破查询并从TLE_TITLE_TOKENS表中进行简单选择时,我得到280,000条记录。
select ttt.dn_wrk_cre_surr_id
from tle_title_tokens ttt
where ttt.token_type in ('E','N')
and ttt.search_token LIKE 'BELIEVE'||'%'
如何阻止它在WORKS表上执行FULL TABLE扫描。我可以对查询提出一些提示,但我认为Oracle会非常聪明地知道在没有提示的情况下使用索引。
同样在TLE_TITLE_TOKENS表上,最好在SEARCH_TOKEN列上创建基于函数的索引,因为用户似乎在此字段上执行LIKE%搜索。基于功能的索引会是什么样的。
我在Oracle 11g数据库上运行。
提前感谢任何答案。
答案 0 :(得分:1)
首先,使用join
:
SELECT wrk.cre_surr_id
FROM tle_title_tokens ttt JOIN
works wrk
ON ttt.dn_wrk_cre_surr_id = wrk.cre_surr_id
WHERE wrk.logically_deleted_y IS NULL AND
ttt.token_type in ('E', 'N') AND
ttt.search_token LIKE 'BELIEVE'||'%';
您应该能够使用索引加快此查询速度。目前尚不清楚最佳指数是什么。我建议使用tle_title_tokens(search_token, toekn_type, dn_wrk_cre_surr_id)
和works(cre_surr_id, logically_deleted_y)
。
另一种可能性是使用EXISTS
编写查询,例如:
SELECT wrk.cre_surr_id
FROM works wrk
WHERE wrk.logically_deleted_y IS NULL AND
EXISTS (SELECT 1
FROM tle_title_tokens ttt
WHERE ttt.dn_wrk_cre_surr_id = wrk.cre_surr_id AND
ttt.token_type IN ('N', 'E') AND
ttt.search_token LIKE 'BELIEVE'||'%'
) ;
对于此版本,您需要works(logically_deleted_y, cre_surr_id)
和tle_title_tokens(dn_wrk_cre_surr_id, token_type, search_token)
上的索引。
答案 1 :(得分:0)
试试这个:
SELECT /*+ leading(ttt) */ wrk.cre_surr_id
FROM works wrk,
tle_title_tokens ttt
WHERE ttt.dn_wrk_cre_surr_id = wrk.cre_surr_id
AND wrk.logically_deleted_y IS NULL
AND ttt.token_type in ('E','N')
AND ttt.search_token LIKE 'BELIEVE'||'%'
答案 2 :(得分:0)
LE_TITLE_TOKENS中的19,923,002行,
有多少记录有TOKEN_TYPE' E'有多少记录有' N'?还有其他TokenTypes吗?如果是,那么它们组合在一起多少?
如果E和N放在一起形成总记录的一小部分,那么检查是否为该列更新了直方图统计数据。
执行计划取决于给定过滤器的20M记录中从LE_TITLE_TOKENS中选择的记录数。
答案 3 :(得分:0)
我假设这个索引定义
create index works_idx on works (cre_surr_id,logically_deleted_y);
create index title_tokens_idx on tle_title_tokens(search_token,token_type,dn_wrk_cre_surr_id);
通常有两种可能的方案来执行连接
NESTED LOOPS 使用索引访问内部表WORKS
,但是在外部表中的每一行循环中重复
HASH JOIN 使用FULL SCAN访问WORKS
,但只能访问一次。
不可能说一个选项不好而另一个选项不错。
如果外部表中只有很少的行(很少的循环),嵌套循环会更好,但外部表(TOKEN
)中记录数量的增加会变慢
HASH JOIN更慢,并且在某个行数处是合适的。
如何看待哪个执行计划更好? 简单强制Oracle使用提示运行两个scanarios并比较已用时间。
在您的情况下,您应该看到这两个执行计划
HASH JOIN
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 207K| 10M| | 2439 (1)| 00:00:30 |
|* 1 | HASH JOIN | | 207K| 10M| 7488K| 2439 (1)| 00:00:30 |
|* 2 | INDEX RANGE SCAN | TITLE_TOKENS_IDX | 207K| 5058K| | 29 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| WORKS | 893K| 22M| | 431 (2)| 00:00:06 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TTT"."DN_WRK_CRE_SURR_ID"="WRK"."CRE_SURR_ID")
2 - access("TTT"."SEARCH_TOKEN" LIKE 'BELIEVE%')
filter("TTT"."SEARCH_TOKEN" LIKE 'BELIEVE%' AND ("TTT"."TOKEN_TYPE"='E' OR
"TTT"."TOKEN_TYPE"='N'))
3 - filter("WRK"."LOGICALLY_DELETED_Y" IS NULL)
NESTED LOOPS
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 207K| 10M| 414K (1)| 01:22:56 |
| 1 | NESTED LOOPS | | 207K| 10M| 414K (1)| 01:22:56 |
|* 2 | INDEX RANGE SCAN| TITLE_TOKENS_IDX | 207K| 5058K| 29 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN| WORKS_IDX | 1 | 26 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("TTT"."SEARCH_TOKEN" LIKE 'BELIEVE%')
filter("TTT"."SEARCH_TOKEN" LIKE 'BELIEVE%' AND
("TTT"."TOKEN_TYPE"='E' OR "TTT"."TOKEN_TYPE"='N'))
3 - access("TTT"."DN_WRK_CRE_SURR_ID"="WRK"."CRE_SURR_ID" AND
"WRK"."LOGICALLY_DELETED_Y" IS NULL)
我的好处是(有280K循环)散列连接(即 FULLTABLE SCAN )将会更好,但可能是你认识到应该使用嵌套循环。 在这种情况下,优化不会正确识别嵌套循环和散列连接之间的切换点。 导致这种情况的常见原因是错误或缺少系统统计信息或不正确的优化器参数。