挑战like查询常识

caweny 2008-01-22 06:09:02
一般的常识认为类似 field like '%aaa%' 的查询是无法用到field字段的索引的。
不过今天看oracle的执行计划发现不是这样的。

sql语句如下的时候:
SELECT count(*) FROM mytable WHERE code LIKE UPPER('%20071119%')
得到的Plan如下:
SELECT STATEMENT CHOOSECost: 41 Bytes: 22 Cardinality: 1
2 SORT AGGREGATE Bytes: 22 Cardinality: 1
1 INDEX FAST FULL SCAN NON-UNIQUE Index_Mytable_Code Cost: 41 Bytes: 85,844 Cardinality: 3,902
显然是用了建在code 上的索引。。。
各位大虾,不知道这是何解????

但是下面的这个sql语句就是另外一种情况:
SELECT SUM(amount) sum FROM mytable WHERE code LIKE UPPER('%20071119%')
它得到的plan如下:
Plan
SELECT STATEMENT CHOOSECost: 4,547 Bytes: 25 Cardinality: 1
2 SORT AGGREGATE Bytes: 25 Cardinality: 1
1 TABLE ACCESS FULL NEWMAN.Comm_Contract Cost: 4,547 Bytes: 97,550 Cardinality: 3,902
这个就用了一个全表扫描,速度非常慢,符合我的常识。。。

所以就有了两个问题:
1、为什么第一句sql用到了索引?
2、如何让第二句也用上索引?
...全文
315 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
tinhoo_tinhoo 2010-10-23
  • 打赏
  • 举报
回复
言之有理
zhangxf1981 2008-01-25
  • 打赏
  • 举报
回复
4楼5楼分析的很对
rockywu 2008-01-25
  • 打赏
  • 举报
回复
高啊,收藏学习
caweny 2008-01-23
  • 打赏
  • 举报
回复
难道是我没看明白吗?fenixshadow先别生气


我的认识是这样的,之所以
SELECT SUM(amount) sum FROM mytable WHERE code LIKE UPPER('%20071119%')
没有使用索引,是oracle在基于代价的CBO方式下,认为这句语句用全表扫描的效果要好于用索引来查然后再用rowid获得数据的方式。所以就选择进行了一次全表扫描。
因此有如下的解决方案:
1、对表进行重新分析,让oracle找到更好的执行计划。
2、建一个包括code和amount的组合索引,以改变oracle的执行计划,让oracle用这个索引进行查找。
3、用hint的方式强制oracle使用索引。

我现在的问题是除了这几种外,还有没有什么方法??
凤影 2008-01-23
  • 打赏
  • 举报
回复
你还是没有好好看我的回复,很生气。
凤影 2008-01-23
  • 打赏
  • 举报
回复
我看过了你没有500分,忽悠我...

不过你态度真的是好啊。

那就再多说两句:

1、首先你的动机或者说目标是有问题的,你的目标是让这个sql使用索引,而不是让这个sql跑的更快或者占用更少的系统资源比如说锁。(听说过南辕北辙么?)
实际上,如果你建过索引,并且进行了表分析以后,oracle要进行全表扫描而不是使用索引的话,就让它进行全表扫描,不要怕。
2、如果确实oracle做了愚蠢的决定,的确应该使用索引而不是全表扫描的话。(这种情况很少,在做出这个判断的时候要做很多数据收集工作而不是凭借喜好)
所谓的应该使用索引至少包括两个方面:sql跑的更快或者占用更少的系统资源。
那么你就通过hint告诉它使用索引。
如果你因为某些原因不能使用hint的话,那么oracle还提供了很多参数来影响执行计划:
optimizer_index_caching、optimizer_index_cost_adj等等等等。
3、扩展开来说,为了获得更好的性能,你还可以重新设计表,比如使用索引组织表、分区,对经常查询的聚合数据建立聚合表等等。但是这些都要在对业务很清楚的情况下才能做,比如我现在就做不了,因为不知道你的需求。
4、没有度量不要进行性能调整。这是最佳实践。

一定要记住你的目标,目标决定一切。当然了可以用更漂亮的话来说:需求决定设计。
ydlchina 2008-01-23
  • 打赏
  • 举报
回复
严重关注
caweny 2008-01-23
  • 打赏
  • 举报
回复
多谢两位的解答,使我对索引的认识进一步加强了。

整理补充几个说明,
1、code是编码,编码中包括了日期的信息,条件是对编码的查询,因此无法用日期字段来解决。
2、已经对表进行了分析。

现在的问题是,
SELECT SUM(amount) sum FROM mytable WHERE code LIKE UPPER('%20071119%')
在我强制使用索引的 时候,也就是改成
SELECT /*+index(mytable)*/ SUM(amount) sum FROM mytable WHERE code LIKE UPPER('%20071119%')
的时候使用因为使用了索引,速度快了很多。

但是因为用了一些中间件的缘故,我无法使用强制使用索引。那该如何进行性能优化呢?除了建立包括code和amount在内的索引。
caweny 2008-01-23
  • 打赏
  • 举报
回复
好,就500分。。。请帮我再深入分析分析。。。


可能是我举例不恰当,不是说刚好会是一个日期的,什么可能性都存在,例如 code LIKE UPPER('%QB2C-2000%'),所以不能做operateTime = to_date('20071119','YYYYMMDD') 这样的更改。

凤影 2008-01-23
  • 打赏
  • 举报
回复
1、再给500分帮你深入分析...

2、你上面总结的不错。

3、你还是没有理解使用日期类型的必要性。比如这里code中包括日期是没错啦,但是应该把查询改为:

SELECT SUM(amount) sum FROM mytable WHERE  operateTime = to_date('20071119','YYYYMMDD');

或者类似的形式。这样才是解决问题的根本方法。
凤影 2008-01-23
  • 打赏
  • 举报
回复
回复楼主:
首先跟你讲几个常识:1、使用索引不一定比全表扫描好。2、尽量用日期类型保存日期。3、索引扫描获得的是记录的rowid,如果需要访问具体的记录则要进行一次TABLE ACCESS BY INDEX ROWID 。4、分析表很关键。

下面所有分析都是在你已经对表进行分析过后的讨论,如果不知道什么是表分析,googleit。需要b树的知识:
对于第一个sql
SELECT count(*) FROM mytable WHERE code LIKE UPPER('%20071119%')

SELECT STATEMENT CHOOSECost: 41 Bytes: 22 Cardinality: 1
2 SORT AGGREGATE Bytes: 22 Cardinality: 1
1 INDEX FAST FULL SCAN NON-UNIQUE Index_Mytable_Code Cost: 41 Bytes: 85,844 Cardinality: 3,902

这里为什么会用索引呢?

仔细看可以发现使用的是INDEX FAST FULL SCAN,这种scan有两个特点:多块读取,读取所有索引块包括内部块和叶块。所以可以通过INDEX FAST FULL SCAN来代替全表扫描来计算count(*):首先是只要遍历一遍索引Index_Mytable_Code就可以确定有多少记录满足 code LIKE UPPER('%20071119%'),而不需要进行TABLE ACCESS BY INDEX ROWID找具体的记录;其次和全表扫描一样都是多块读取。所以无论如何都比全表扫描快,自然oracle会使用。


下面的这个sql语句就是另外一种情况:
SELECT SUM(amount) sum FROM mytable WHERE code LIKE UPPER('%20071119%')

Plan
SELECT STATEMENT CHOOSECost: 4,547 Bytes: 25 Cardinality: 1
2 SORT AGGREGATE Bytes: 25 Cardinality: 1
1 TABLE ACCESS FULL NEWMAN.Comm_Contract Cost: 4,547 Bytes: 97,550 Cardinality: 3,902

这里为什么没有使用索引呢,首先看看能不能使用上面提到的INDEX FAST FULL SCAN,因为这里要计算 SUM(amount) ,这样的话就必须要得到具体的记录才行,而不是仅仅获得数量。就是说如果使用这个索引,还要进行一次TABLE ACCESS BY INDEX ROWID,这样就不一定比全表扫描快了。

对比看一下楼上对此sql强制使用索引的plan:
------------------------------------------------------------------------------------------
¦ Id ¦ Operation ¦ Name ¦ Rows ¦ Bytes ¦ Cost (%CPU) ¦ Time ¦
------------------------------------------------------------------------------------------
¦ 0 ¦ SELECT STATEMENT ¦ ¦ 1 ¦ 30 ¦ 195 (4) ¦ 00:00:01 ¦
¦ 1 ¦ SORT AGGREGATE ¦ ¦ 1 ¦ 30 ¦ ¦ ¦
¦ 2 ¦ TABLE ACCESS BY INDEX ROWID ¦ T ¦ 8 ¦ 240 ¦ 195 (4) ¦ 00:00:01 ¦
¦* 3 ¦ INDEX FULL SCAN ¦ IDX_OWNER ¦ 2773 ¦ ¦ 126 (6) ¦ 00:00:01 ¦
------------------------------------------------------------------------------------------

比使用全表扫表多了两个步骤: INDEX FULL SCAN 、 TABLE ACCESS BY INDEX ROWID。
第一个步骤INDEX FULL SCAN是对索引的一个扫描,它有两个特点:扫描所有的叶子节点(比INDEX FAST FULL SCAN少扫描了内部节点),一次一块的读取。
第二个步骤TABLE ACCESS BY INDEX ROWID是再上一个步骤得到需要的记录的ROWID后,使用ROWID获得具体的记录。

概念已经有了,那么为什么oracle默认没有使用索引呢?举个极端的例子,假设所有的记录都满足 code LIKE UPPER('%20071119%'),则:
1、使用全表扫描:多块读取所有记录块。
2、使用上述索引:单块读取所有叶子节点、读取所有记录块。
看到没有?在这种极端的情况下,使用索引会比全表扫描多整整一步(单块读取所有叶子节点),而且第二步只会比全表扫描一次慢不会比全表扫描快。


好了,希望能让楼主能有一些概念了。

总结一下再:1、使用索引不一定比全表扫描好,通过上面的例子应该明确了。2、尽量用日期类型保存日期,这里就不多说了,但是能影响性能和数据完整性。4、分析表很关键,上面的例子只是说明了可能全表扫描比索引好,当然也可能不好,那我们怎么办呢?答案就是分析表,分析表可以帮助oracle做出正确的判断。

ps:3楼的问题作为家庭作业^-^。
tom_cheung 2008-01-22
  • 打赏
  • 举报
回复
1、为什么第一句sql用到了索引?

SELECT count(*) FROM mytable WHERE code LIKE UPPER('%20071119%')
这个sql的所有记录都可以在一个index里面得到,使用的是INDEX FAST FULL SCAN ,就是把这个index当作一个表使用,这个index建立在code列上,所有符合条件的code列都在index上面,所以就没必要使用表。

2、如何让第二句也用上索引?
SELECT SUM(amount) sum FROM mytable WHERE code LIKE UPPER('%20071119%')
amount列不在index中,如果也使用index scan,必须从index获得rowid再去表中获得amout列的值,oracle认为代价可能比全表scan还高,所以选择table scan
要使用index scan 可以在code和amount上面建立index,就可以使用index fast full scan

T@ora>SELECT COUNT(*) FROM T WHERE OWNER LIKE UPPER('%20071119%') ;
Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 4149176714

-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 17 | 72 (7)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 17 | | |
|* 2 | INDEX FAST FULL SCAN| IDX_OWNER | 8 | 136 | 72 (7)| 00:00:01 |
-----------------------------------------------------------------------------------

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

2 - filter("OWNER" LIKE '%20071119%')

Note
-----
- dynamic sampling used for this statement

默认不走index scan
T@ora>SELECT SUM(OBJECT_ID) FROM T WHERE OWNER LIKE UPPER('%20071119%') ;
Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 369 (3)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | 30 | | |
|* 2 | TABLE ACCESS FULL| T | 8 | 240 | 369 (3)| 00:00:02 |
---------------------------------------------------------------------------

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

2 - filter("OWNER" LIKE '%20071119%')

Note
-----
- dynamic sampling used for this statement

加hint 强制indexscan
T@ora>SELECT/*+index(t)*/ SUM(OBJECT_ID) FROM T WHERE OWNER LIKE UPPER('%20071119%') ;
Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 1633656054

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 195 (4)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 30 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T | 8 | 240 | 195 (4)| 00:00:01 |
|* 3 | INDEX FULL SCAN | IDX_OWNER | 2773 | | 126 (6)| 00:00:01 |
------------------------------------------------------------------------------------------

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

3 - filter("OWNER" LIKE '%20071119%')

Note
-----
- dynamic sampling used for this statement

建多列index,就可以index scan
T@ora>create index idx_OWNER_OBJECT_ID on t(owner,object_id);

Index created.

Elapsed: 00:00:00.29
T@ora>SELECT SUM(OBJECT_ID) FROM T WHERE OWNER LIKE UPPER('%20071119%') ;
Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
Plan hash value: 3716430985

---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 89 (6)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 30 | | |
|* 2 | INDEX FAST FULL SCAN| IDX_OWNER_OBJECT_ID | 8 | 240 | 89 (6)| 00:00:01 |
---------------------------------------------------------------------------------------------

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

2 - filter("OWNER" LIKE '%20071119%')

Note
-----
- dynamic sampling used for this statement

T@ora>
caweny 2008-01-22
  • 打赏
  • 举报
回复
还发现:
SELECT ID FROM mytable WHERE code LIKE UPPER('%20071119%')
的计划如下:

SELECT STATEMENT CHOOSECost: 795 Bytes: 105,354 Cardinality: 3,902
4 VIEW NEWMAN.index$_join$_001 Cost: 795 Bytes: 105,354 Cardinality: 3,902
3 HASH JOIN Bytes: 105,354 Cardinality: 3,902
1 INDEX FAST FULL SCAN UNIQUE PK_mytable_ID Cost: 236 Bytes: 105,354 Cardinality: 3,902
2 INDEX FAST FULL SCAN NON-UNIQUE Index_Mytable_Code Cost: 236 Bytes: 105,354 Cardinality: 3,902
用到了索引 ,速度非常快。

但是
SELECT ID,amount FROM mytable WHERE code LIKE UPPER('%20071119%')
的计划就是
SELECT STATEMENT CHOOSECost: 4,547 Bytes: 117,060 Cardinality: 3,902
1 TABLE ACCESS FULL mytable Cost: 4,547 Bytes: 117,060 Cardinality: 3,902
就没用到索引,速度非常的慢
vc555 2008-01-22
  • 打赏
  • 举报
回复
关注
caweny 2008-01-22
  • 打赏
  • 举报
回复
我用的是oracle 9i

679

社区成员

发帖
与我相关
我的任务
社区描述
智能路由器通常具有独立的操作系统,包括OpenWRT、eCos、VxWorks等,可以由用户自行安装各种应用,实现网络和设备的智能化管理。
linuxpython 技术论坛(原bbs)
社区管理员
  • 智能路由器社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧