一、结论
为什么有的时候SQL语句会走索引,有的会走全表扫描,这是根据COST成本来判断的,也就是说当全表扫描的花费成本比走索引的底,那走全表扫描很正常。
当你要查询的这个列符合条件的数值,当他的值大于等于所占比例其中的9成时,那还不如不走索引,直接全表扫描更快。
二、基础知识
序号 | 命令 | 解释 |
1 | set autotrace off | 默认值,关闭Autotrace |
2 | set autotrace on explan | 只显示执行计划 |
3 | set autotrace on statistics | 只显示执行的统计计划 |
4 | set autotrace on | 包含2、3两项内容 |
5 | set autotrace traceonly | 与ON相似,但是不显示语句的执行结果 |
6 | set timing on | 显示执行时间 |
查看数据库SQL语句真实的执行计划需要通过上述命令来达到,主要用到5和6
三、SQL语句信息
1、全表扫描(当un列和gs列具体的数值不一样时)
SELECT yt.km bh,
SUM(yt.jf) nc
FROM mj_cx yt
WHERE cl = 101
AND (un = '18')
AND (fl = '01')
AND (gs = 'G')
GROUP BY km;
2、使用索引——INX(当un列和gs列具体的数值不一样时)
SELECT yt.km bh,
SUM(yt.jf) nc
FROM mj_cx yt
WHERE cl = 101
AND (un = '30')
AND (fl = '01')
AND (gs = 'S')
GROUP BY km;
3、表的收集统计信息时间(时间很近,也就是说和统计信息无关)
SQL> select last_analyzed from dba_tables where table_name='MJ';
LAST_ANALYZE
------------
17-APR-23
4、该表拥有索引,其中跟语句相关的条件列,只有 INX 和 INX_CX
create index INX on mj_cx (CL,UN,GS,FL,CO,KM,BK,WB);
create index INX_CX on mj_cx (CL,UN,GS,FL,KM);
create index INX_CX1 on mj_cx (CL,UN,FL,KM);
create index INX_CX2 on mj_cx (CL,UN,FL,CO,KM);
创建一个索引专属于四列
create index INX_CX3 on mj_cx(CL, UN, FL,GS);
四、判断思路
1、这个是创建了只属于四个列的合适索引的执行sql语句——INX_CX3
Execution Plan
----------------------------------------------------------
Plan hash value: 3169905893
--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16162 | 615K | 10231 (1) | 00:00:01 |
| 1 | HASH GROUP BY | | 16162 | 615K | 10231 (1) | 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED | MJ | 75837 | 2888K | 10228 (1) | 00:00:01 |
|* 3 | INDEX RANGE SCAN | INX_CX3 | 75837 | | 333 (0) | 00:00:01 |
--------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("F_CLIENT"=101 AND "F_UNITID"='18' AND "F_FLZBH"='01' AND "F_GSDMBH"='G')
Statistics
----------------------------------------------------------
3 recursive calls
0 db block gets
13623 consistent gets
0 physical reads
0 redo size
119375 bytes sent via SQL*Net to client
3314 bytes received via SQL*Net from client
248 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3698 rows processed
###########################################################################################################################
2、走的索引——INX
select /*+index(t pk_emp)*/* from emp t
--强制索引,/*.....*/第一个星星后不能有空格,里边内容结构为:加号index(表名 空格 索引名)。
--如果表用了别名,注释里的表也要使用别名
SELECT /*+index(yt INX)*/yt.km bh,
SUM(yt.jf) nc
FROM mj_cx yt
WHERE cl = 101
AND (un = '18')
AND (fl = '01')
AND (gs = 'G')
GROUP BY km;
Execution Plan
----------------------------------------------------------
Plan hash value: 1540358647
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16162 | 615K | 40262 (1)| 00:00:02 |
| 1 | HASH GROUP BY | | 16162 | 615K | 40262 (1)| 00:00:02 |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| MJ | 75837 | 2888K | 40259 (1)| 00:00:02 |
|* 3 | INDEX RANGE SCAN | INX | 75837 | | 761 (1) | 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("CL"=101 AND "UN"='18' AND "GS"='G' AND "FL"='01')
Statistics
----------------------------------------------------------
2 recursive calls
0 db block gets
81927 consistent gets
1085 physical reads
0 redo size
119375 bytes sent via SQL*Net to client
3314 bytes received via SQL*Net from client
248 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3698 rows processed
###########################################################################################################################
3、走的索引——INX_CX
SELECT /*+index(yt INX_CX)*/yt.km bh,
SUM(yt.jf) nc
FROM mj_cx yt
WHERE cl = 101
AND (un = '18')
AND (fl = '01')
AND (gs = 'G')
GROUP BY km;
Execution Plan
----------------------------------------------------------
Plan hash value: 3877613795
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16162 | 615K | 72878 (1) | 00:00:03 |
| 1 | SORT GROUP BY NOSORT | | 16162 | 615K | 72878 (1) | 00:00:03 |
| 2 | TABLE ACCESS BY INDEX ROWID | MJ | 75837 | 2888K| 72878 (1) | 00:00:03 |
|* 3 | INDEX RANGE SCAN | INX_CX | 75837 | | 490 (0) | 00:00:01 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("CL"=101 AND "UN"='18' AND "GS"='G' AND "FL"='01')
Statistics
----------------------------------------------------------
2 recursive calls
0 db block gets
103294 consistent gets
695 physical reads
0 redo size
128327 bytes sent via SQL*Net to client
3314 bytes received via SQL*Net from client
248 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3698 rows processed
###########################################################################################################################
4、强制全表扫描
表名
(1) 若表有 '别名',则是 '别名'
(2) 若表没有 '别名',则是 '表名' 全称
SELECT /*+ full(yt)*/yt.km bh,
SUM(yt.jf) nc
FROM mj_cx yt
WHERE cl = 101
AND (un = '18')
AND (fl = '01')
AND (gs = 'G')
GROUP BY km;
Execution Plan
----------------------------------------------------------
Plan hash value: 1975139930
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16162 | 615K | 10542 (1) | 00:00:01 |
| 1 | HASH GROUP BY | | 16162 | 615K | 10542 (1) | 00:00:01 |
|* 2 | TABLE ACCESS FULL | MJ | 75837 | 2888K| 10540 (1) | 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("UN"='18' AND "GS"='G' AND "CL"=101 AND "FL"='01')
Statistics
----------------------------------------------------------
6 recursive calls
0 db block gets
392606 consistent gets
0 physical reads
0 redo size
119375 bytes sent via SQL*Net to client
3314 bytes received via SQL*Net from client
248 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3698 rows processed
5、通过表格排序
序号 | sql语句执行使用索引情况 | 涉及列 | 花费成本 | 花费最少排序 |
1 | INX_CX3(四列索引) | CL, UN, FL, GS | 10231 | 第一 |
2 | INX(八列索引) | CL, UN, GS, FL, CO, KM, BK, WB | 40262 | 第三 |
3 | INX_CX(五列索引) | CL, UN, GS, FL, KM | 72878 | 第四 |
4 | 强制全表扫描 | 10542 | 第二 |
有意思的是 走专属的 INX_CX3(四列索引)其实就比全表扫描 Cost花费少 311,不管是花费还是执行时间都相差不多
6、梳理解释
select distinct cl,count(*) from mj_cx group by cl;
select distinct un,count(*) from mj_cx group by un;
select distinct fl,count(*) from mj_cx group by fl;
select distinct gs,count(*) from mj_cx group by gs;
两个sql语句的得出数量值不一样,相差五倍
全表扫描 107850
走索引 22228
当某列查询数据时全表扫描和索引扫描相差不多时(甚至索引产生的伪列 rowid,产生IO)
CL count(*)
1 101 354165
UN COUNT(*)
2 30 22228
9 18 107850
FL COUNT(*)
1 01 354165
GS COUNT(*)
1 S 22228
3 G 249040
走全表扫描时,distinct值不明显
select count(*) from ackmje_cx2022 where f_gsdmbh='G'
249040
也就是说当你要查询的这个列符合条件的数值,当他的值大于等于所占比例其中的9成时,那还不如不走索引,直接全表扫描更快