SQL优化-同SQL不同执行计划(一)
2008-05-14 20:36
357 查看
问题来源:CSDN Oracle技术论坛
问题 :myepoch提出相同SQL因为检索的值,不同执行效率差别巨大。
问题环境:Oracle 10g,TBNC_P表在adminaccount有B树索引,
TBNC_P总记录数:33422行
TBNC_A总记录数:40782行
结果 :已经解决,优化后的SQL执行时间,均小于0.032秒
【SQL 1】:
[align=left]select art.article_id, art.article_title, aps.adminaccount[/align]
[align=left] from TBNC_A art, TBNC_P aps[/align]
[align=left] where art.column_id = aps.scopestr[/align]
[align=left] and aps.funcnodepath = 'A001B002C002D002E003'[/align]
and aps.adminaccount = 'lgm';
【SQL 2】:
[align=left]select art.article_id, art.article_title, aps.adminaccount[/align]
[align=left] from TBNC_A art, TBNC_P aps[/align]
[align=left] where art.column_id = aps.scopestr[/align]
[align=left] and aps.funcnodepath = 'A001B002C002D002E003'[/align]
and aps.adminaccount = 'admin_two';
两个SQL除了【aps.adminaccount】等于的值不同,其他完全一致,
执行时间(平均) 执行结果数
【SQL 1】 3.906 S 148行
【SQL 2】 0.531 S 11421 行
执行计划如下:
【SQL1】
3 -----------------------------------------------------------------------------------------------------
4 ¦ Id ¦ Operation ¦ Name ¦ Rows ¦ Bytes ¦ Cost (%CPU) ¦ Time ¦
5 -----------------------------------------------------------------------------------------------------
6 ¦ 0 ¦ SELECT STATEMENT ¦ ¦ 888 ¦ 65712 ¦ 4291 (1) ¦ 00:00:52 ¦
7 ¦* 1 ¦ HASH JOIN ¦ ¦ 888 ¦ 65712 ¦ 4291 (1) ¦ 00:00:52 ¦
8 ¦* 2 ¦ TABLE ACCESS BY INDEX ROWID ¦ POWERSCOPE ¦ 11 ¦ 330 ¦ 3 (0) ¦ 00:00:01 ¦
9 ¦* 3 ¦ INDEX RANGE SCAN ¦ SYS_LGM ¦ 87 ¦ ¦ 1 (0) ¦ 00:00:01 ¦
10 ¦ 4 ¦ TABLE ACCESS FULL ¦ CMSARTICLE ¦ 40782 ¦ 1752K ¦ 4288 (1) ¦ 00:00:52 ¦
11 -----------------------------------------------------------------------------------------------------
【SQL2】
3 -------------------------------------------------------------------------------------------
4 ¦ Id ¦ Operation ¦ Name ¦ Rows ¦ Bytes ¦ Cost (%CPU) ¦ Time ¦
5 -------------------------------------------------------------------------------------------
6 ¦ 0 ¦ SELECT STATEMENT ¦ ¦ 44229 ¦ 3196K ¦ 4344 (1) ¦ 00:00:53 ¦
7 ¦* 1 ¦ HASH JOIN ¦ ¦ 44229 ¦ 3196K ¦ 4344 (1) ¦ 00:00:53 ¦
8 ¦* 2 ¦ TABLE ACCESS FULL ¦ POWERSCOPE ¦ 541 ¦ 16230 ¦ 56 (2) ¦ 00:00:01 ¦
9 ¦ 3 ¦ TABLE ACCESS FULL ¦ CMSARTICLE ¦ 40782 ¦ 1752K ¦ 4288 (1) ¦ 00:00:52 ¦
10 -------------------------------------------------------------------------------------------
【疑惑四】【执行计划成本】等价于【执行时间】
【疑惑三】SQL1检索的数据比SQL2的少,为什还比SQL2慢?
【疑惑二】SQL1中使用了索引【SYS_LGM】反而还比SQL2【全表扫描】慢?
【疑惑一】相同SQL语句,为什么存在【执行计划】的差别?
在SQL优化的方面没有什么定式,带着上述问题,开始在没有测试环境的前提下,开始和myepoch一起排查测试,寻找问题。
【疑惑一解答】
大多数人都会一样这样观点,相同SQL执行计划应该是相同的,这样的观点是错误,上面的问题就是很好的佐证,数据分布和存储特点都会影响执行计划的选择,Oracle选择的执行计划在统计信息正确的前提下,99%都不会有问题。
我做一个不算恰当的假设:
假设我们有一个表,叫TableA,它有三个字段分别是ColA,ColB,ColC,表没有主键,所有字段都非空。每条记录存储在一个数据块中。
ColB值只能是('sys','user','costomer')这三种的其中一种,数据表中有一万条记录。
ColB数据的分布分别是(2%,95%,3%)
再假设,我们在ColB上有一个B树索引。
如果我们写这样的Sql:
SELECT *
FROM TableA
where ColB = 'user'
你说上面的SQL一定会使用在ColB上的索引吗? 结果是【不一定】
Oracle的执行计划会是根据统计分析做出的,执行计划会随着数据的变化而变化。
以上SQL多数时候是不使用索引的,因为Select字段的*影响了执行计划,
假设Oralce通过索引进行快速扫描,假设它会执行9500次检索,但为了取得ColA和ColC数据Oracle不得不在通过 Rowid获取数据,获取数据需要9500次,这样Oracle 就需要 19000次操作才能完成工作。
但要使用全表扫描只需要10000次就可以搞定了,所以这时候Oracle会选择全表扫描。不会选择使用索引。
当让Oralce不会执行10000次,它会通过好多种方法和手段去尽快完成工作,上面的说明只是方便说明而已,
但是基本原理相当。
如果我们改变SQL语句:
SELECT ColB
FROM TableA
where ColB = 'user'
你说Oracle还会选择【索引快速扫描】和【全表扫描】那种执行计划呢?
结果肯定是【索引快速扫描】。
如果我们再改变一下SQL语句:
SELECT *
FROM TableA
where ColB = 'sys'
你说Oracle 优化器会选择【索引快速扫描】和【全表扫描】那种执行计划呢?
结果是【索引快速扫描】,而不是【全表扫描】。
原因很简单,如果先通过【索引快速扫描】寻找到2%的数据的Rowid,然后在通过Rowid找到ColA和ColB的记录,
明显比【全表扫描】快,Oralce的代价不过400而已。
所以可以看出,Where不光是影响索引使用的唯一条件,Select也会影响索引使用。千万比忘记了这一点,很重要。
绝大多数时候Oracle自己可以自动收集【统计信息】,所以保证数据【统计信息】非常重要。
【统计信息】会影响【执行计划】。
综合上述,需要确认【统计信息】是否正确,确认语句如下:
select user_tables.num_rows,user_tables.last_analyzed,user_tables.*
from user_tables
where user_tables.table_name
in ('TBNC_A','TBNC_P');
更具上述分析,所以请求myepoch提供了【统计信息】,并且手动启动了【统计信息】包。
执行语句如下:
dbms_stats.gather_table_stats(ownname => '',tabname => 'TBNC_A');
【统计信息】包执行前,如下(摘要):
表名 记录数 最后统计时间 数据块数
TBNC_P 33422 2008-5-13 244
TBNC_A 40782 2008-2-15 19535
【统计信息】包执行后,如下(摘要):
表名 记录数 最后统计时间 数据块数
TBNC_P 33422 2008-5-13 244
TBNC_A 40782 2008-5-14 19535
统计后【SQL1】和【SQL2】未发生任何变化,判读错误,疑点排除,错误不在【执行计划】处。
接-> SQL优化-同SQL不同执行计划(二)
2008-05-14晚 凌蓝风
问题 :myepoch提出相同SQL因为检索的值,不同执行效率差别巨大。
问题环境:Oracle 10g,TBNC_P表在adminaccount有B树索引,
TBNC_P总记录数:33422行
TBNC_A总记录数:40782行
结果 :已经解决,优化后的SQL执行时间,均小于0.032秒
【SQL 1】:
[align=left]select art.article_id, art.article_title, aps.adminaccount[/align]
[align=left] from TBNC_A art, TBNC_P aps[/align]
[align=left] where art.column_id = aps.scopestr[/align]
[align=left] and aps.funcnodepath = 'A001B002C002D002E003'[/align]
and aps.adminaccount = 'lgm';
【SQL 2】:
[align=left]select art.article_id, art.article_title, aps.adminaccount[/align]
[align=left] from TBNC_A art, TBNC_P aps[/align]
[align=left] where art.column_id = aps.scopestr[/align]
[align=left] and aps.funcnodepath = 'A001B002C002D002E003'[/align]
and aps.adminaccount = 'admin_two';
两个SQL除了【aps.adminaccount】等于的值不同,其他完全一致,
执行时间(平均) 执行结果数
【SQL 1】 3.906 S 148行
【SQL 2】 0.531 S 11421 行
执行计划如下:
【SQL1】
3 -----------------------------------------------------------------------------------------------------
4 ¦ Id ¦ Operation ¦ Name ¦ Rows ¦ Bytes ¦ Cost (%CPU) ¦ Time ¦
5 -----------------------------------------------------------------------------------------------------
6 ¦ 0 ¦ SELECT STATEMENT ¦ ¦ 888 ¦ 65712 ¦ 4291 (1) ¦ 00:00:52 ¦
7 ¦* 1 ¦ HASH JOIN ¦ ¦ 888 ¦ 65712 ¦ 4291 (1) ¦ 00:00:52 ¦
8 ¦* 2 ¦ TABLE ACCESS BY INDEX ROWID ¦ POWERSCOPE ¦ 11 ¦ 330 ¦ 3 (0) ¦ 00:00:01 ¦
9 ¦* 3 ¦ INDEX RANGE SCAN ¦ SYS_LGM ¦ 87 ¦ ¦ 1 (0) ¦ 00:00:01 ¦
10 ¦ 4 ¦ TABLE ACCESS FULL ¦ CMSARTICLE ¦ 40782 ¦ 1752K ¦ 4288 (1) ¦ 00:00:52 ¦
11 -----------------------------------------------------------------------------------------------------
【SQL2】
3 -------------------------------------------------------------------------------------------
4 ¦ Id ¦ Operation ¦ Name ¦ Rows ¦ Bytes ¦ Cost (%CPU) ¦ Time ¦
5 -------------------------------------------------------------------------------------------
6 ¦ 0 ¦ SELECT STATEMENT ¦ ¦ 44229 ¦ 3196K ¦ 4344 (1) ¦ 00:00:53 ¦
7 ¦* 1 ¦ HASH JOIN ¦ ¦ 44229 ¦ 3196K ¦ 4344 (1) ¦ 00:00:53 ¦
8 ¦* 2 ¦ TABLE ACCESS FULL ¦ POWERSCOPE ¦ 541 ¦ 16230 ¦ 56 (2) ¦ 00:00:01 ¦
9 ¦ 3 ¦ TABLE ACCESS FULL ¦ CMSARTICLE ¦ 40782 ¦ 1752K ¦ 4288 (1) ¦ 00:00:52 ¦
10 -------------------------------------------------------------------------------------------
【疑惑四】【执行计划成本】等价于【执行时间】
【疑惑三】SQL1检索的数据比SQL2的少,为什还比SQL2慢?
【疑惑二】SQL1中使用了索引【SYS_LGM】反而还比SQL2【全表扫描】慢?
【疑惑一】相同SQL语句,为什么存在【执行计划】的差别?
在SQL优化的方面没有什么定式,带着上述问题,开始在没有测试环境的前提下,开始和myepoch一起排查测试,寻找问题。
【疑惑一解答】
大多数人都会一样这样观点,相同SQL执行计划应该是相同的,这样的观点是错误,上面的问题就是很好的佐证,数据分布和存储特点都会影响执行计划的选择,Oracle选择的执行计划在统计信息正确的前提下,99%都不会有问题。
我做一个不算恰当的假设:
假设我们有一个表,叫TableA,它有三个字段分别是ColA,ColB,ColC,表没有主键,所有字段都非空。每条记录存储在一个数据块中。
ColB值只能是('sys','user','costomer')这三种的其中一种,数据表中有一万条记录。
ColB数据的分布分别是(2%,95%,3%)
再假设,我们在ColB上有一个B树索引。
如果我们写这样的Sql:
SELECT *
FROM TableA
where ColB = 'user'
你说上面的SQL一定会使用在ColB上的索引吗? 结果是【不一定】
Oracle的执行计划会是根据统计分析做出的,执行计划会随着数据的变化而变化。
以上SQL多数时候是不使用索引的,因为Select字段的*影响了执行计划,
假设Oralce通过索引进行快速扫描,假设它会执行9500次检索,但为了取得ColA和ColC数据Oracle不得不在通过 Rowid获取数据,获取数据需要9500次,这样Oracle 就需要 19000次操作才能完成工作。
但要使用全表扫描只需要10000次就可以搞定了,所以这时候Oracle会选择全表扫描。不会选择使用索引。
当让Oralce不会执行10000次,它会通过好多种方法和手段去尽快完成工作,上面的说明只是方便说明而已,
但是基本原理相当。
如果我们改变SQL语句:
SELECT ColB
FROM TableA
where ColB = 'user'
你说Oracle还会选择【索引快速扫描】和【全表扫描】那种执行计划呢?
结果肯定是【索引快速扫描】。
如果我们再改变一下SQL语句:
SELECT *
FROM TableA
where ColB = 'sys'
你说Oracle 优化器会选择【索引快速扫描】和【全表扫描】那种执行计划呢?
结果是【索引快速扫描】,而不是【全表扫描】。
原因很简单,如果先通过【索引快速扫描】寻找到2%的数据的Rowid,然后在通过Rowid找到ColA和ColB的记录,
明显比【全表扫描】快,Oralce的代价不过400而已。
所以可以看出,Where不光是影响索引使用的唯一条件,Select也会影响索引使用。千万比忘记了这一点,很重要。
绝大多数时候Oracle自己可以自动收集【统计信息】,所以保证数据【统计信息】非常重要。
【统计信息】会影响【执行计划】。
综合上述,需要确认【统计信息】是否正确,确认语句如下:
select user_tables.num_rows,user_tables.last_analyzed,user_tables.*
from user_tables
where user_tables.table_name
in ('TBNC_A','TBNC_P');
更具上述分析,所以请求myepoch提供了【统计信息】,并且手动启动了【统计信息】包。
执行语句如下:
dbms_stats.gather_table_stats(ownname => '',tabname => 'TBNC_A');
【统计信息】包执行前,如下(摘要):
表名 记录数 最后统计时间 数据块数
TBNC_P 33422 2008-5-13 244
TBNC_A 40782 2008-2-15 19535
【统计信息】包执行后,如下(摘要):
表名 记录数 最后统计时间 数据块数
TBNC_P 33422 2008-5-13 244
TBNC_A 40782 2008-5-14 19535
统计后【SQL1】和【SQL2】未发生任何变化,判读错误,疑点排除,错误不在【执行计划】处。
接-> SQL优化-同SQL不同执行计划(二)
2008-05-14晚 凌蓝风
相关文章推荐
- SQL优化-同SQL不同执行计划(二)
- SQL优化-同SQL不同执行计划(三)-完
- SQL优化-同SQL不同执行计划-(CLOB详细分析1)
- SQL优化-同SQL不同执行计划-(CLOB详细分析2)
- Sql Server 执行计划及Sql查询优化
- SQL优化--使用 EXISTS 代替 IN 和 inner join来选择正确的执行计划
- 通过分析SQL语句的执行计划优化SQL(五)
- 分析执行计划优化SQLORACLE的执行计划(转)
- oracle 执行计划SQL优化的学习历程
- SQL优化:设置执行计划的显示格式
- 通过分析SQL语句的执行计划优化SQL (四)
- 通过分析SQL语句的执行计划优化SQL(七)(2)
- Sql Server 执行计划及Sql查询优化
- mysql中的SQL优化与执行计划
- 初探Sql Server 执行计划及Sql查询优化
- 通过分析SQL语句的执行计划优化SQL(总结)
- 引用:初探Sql Server 执行计划及Sql查询优化
- 通过分析SQL语句的执行计划优化SQL(总结)
- ORACLE SQL优化 - ORACLE执行计划
- SQL优化【基础02】 - 执行计划的执行先后顺序