问题来源:CSDN Oracle技术论坛
问题
:
myepoch
提出相同
SQL
因为检索的值,不同执行效率差别巨大。
问题环境:
Oracle 10g
,
TBNC_P
表在
adminaccount
有
B
树索引,
TBNC_P
总记录数:
33422
行
TBNC_A
总记录数:
40782
行
结果
:
已经解决,优化后的
SQL
执行时间,均小于
0.032
秒
【
SQL 1
】:
select art.article_id, art.article_title, aps.adminaccount
from TBNC_A art, TBNC_P aps
where art.column_id = aps.scopestr
and aps.funcnodepath = 'A001B002C002D002E003'
and aps.adminaccount = '
lgm
';
【
SQL 2
】:
select art.article_id, art.article_title, aps.adminaccount
from TBNC_A art, TBNC_P aps
where art.column_id = aps.scopestr
and aps.funcnodepath = 'A001B002C002D002E003'
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晚 凌蓝风