如何导致全表扫描原因

转一位大神的笔记。

导致表的执行计划做全表扫描的原因:

u       SQL谓词列没有建相应的索引

u       谓词列建有相应的索引,但执行计划没有使用

Oracle不使用b*tree索引的情况大致如下

1:where条件中和null比较可能导致不使用索引

2:count,sum,ave,max,min等聚集操作时可能导致不使用索引

3:显示或者隐式的函数转换导致不使用索引

4:在cbo模式下,统计信息过于陈旧导致不使用索引

5:组合索引中没有使用前导列导致没有使用索引

6:访问的数据量超过一定的比例导致不使用索引

下面就其中的几点做一些说明

一:Null可以使用索引吗

一般情况下,where条件中和null比较将会导致full table scan,实际上,如果table中索引建列的值都为null,那么该行在索引(此处指b*tree,位图索引和聚簇索引可以有空值)中就不会存在,因此oracle为了保证查询结构的准确性,就会用full table scan代替indexscan,这样理解,不走索引也就在情理之中。

当然,如果某个索引列上有定义为not null,在这种情况下,不存在所有索引列都为空的情况,所以此种情况下,是可以走index scan的,因此,对于where条件中含有类似is null,=null的情况,是否走索引,还是要看索引建中是否有某个列定义为not null。

具体实验如下:

SQL> create table t(x char(3),y char(5));

SQL> insert into t(x,y) values (‘001‘,‘xxxxx‘);

SQL> insert into t(x,y) values (‘002‘,null);

SQL> insert into t(x,y) values (null,‘yyyyy‘);

SQL> insert into t(x,y) values (null,null);

SQL> commit;

SQL> create unique index t_idx on t(x,y);

SQL> analyze table t compute statistics for table for all indexes;

SQL> select blevel,leaf_blocks,num_rows from user_indexes where index_name=upper(‘t_idx‘);

BLEVEL LEAF_BLOCKS  NUM_ROWS

---------- ----------- ----------

0          1         3

isnert四条记录,但索引只保存3条,最后一条没有保存在索引中

SQL> set autotrace traceonly explain;

SQL> select * from t where x is null;

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=8)

1   0  TABLE ACCESS (FULL)OF ‘T‘ (Cost=2 Card=1 Bytes=8)

SQL> create table t1(x char(3),y char(5) not null);

SQL> insert into t1(x,y) values (‘001‘,‘xxxxx‘);

SQL> insert into t1(x,y) values (null,‘xxxxx‘);

SQL> commit;

SQL> create unique index t1_idx on t1(x,y);

SQL> analyze table t1 compute statistics for table for all indexes;

SQL> select blevel,leaf_blocks,num_rows from user_indexes where index_name=upper(‘t1_idx‘);

BLEVEL LEAF_BLOCKS  NUM_ROWS

---------- ----------- ----------

0          1         2

SQL> select * from t1 where x is null;

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1 Bytes=11)

1   0  INDEX (RANGE SCAN) OF ‘T1_IDX‘ (UNIQUE)(Cost=1 Card=1 Byt

二:COUNT(*)等聚集函数可能导致不使用索引

在做count,sum,ave,max,min等聚集操作时,有的时候也会不用索引,因为如果优化器发现索引列没有任何一个列定义为not null,而且where条件中也没有索引键列,如x=x,在此情况下,索引扫描结果会不准确,此时oracle就会用全表full table scan。沿用上面的二个表来说明

SQL> select count(*) from t;

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1)

1   0  SORT (AGGREGATE)

2   1    TABLE ACCESS (FULL) OF ‘T‘(Cost=2 Card=4)

SQL> select sum(x) from t;

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=5)

1   0  SORT (AGGREGATE)

2   1    TABLE ACCESS (FULL) OF ‘T‘(Cost=2 Card=4 Bytes=20)

因为该表的索引列(x,y)没有定义为not null,所以都走了全表扫描,即使把x=null,y=null的行删除,同样还是走全表扫描。

SQL> delete t where x is null and y is null;

已删除1行。

Commit

SQL> analyze table t compute statistics for table for all indexes;

SQL> select count(*) from t;

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1)

1   0  SORT (AGGREGATE)

2   1    TABLE ACCESS (FULL) OF ‘T‘(Cost=2 Card=3)

同样的sql语句,对于t1表,因为索引列y定义为not null,所以oracle会选择index scan

SQL> select count(*) from t1;

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1)

1   0  SORT (AGGREGATE)

2   1    INDEX (FULL SCAN) OF ‘T1_IDX‘ (UNIQUE)(Cost=1 Card=2)

SQL> select sum(x) from t1;

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1 Bytes=5)

1   0  SORT (AGGREGATE)

2   1    INDEX (FULL SCAN) OF ‘T1_IDX‘ (UNIQUE)(Cost=1 Card=2 By

tes=10)

三:隐式或者显示的函数转换降导致全表扫描

SQL> Select * from t1 where x=001;

X     Y         COMM

------ ---------- ----------------------------

001   xxxxx     88888

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=26)

1   0  TABLE ACCESS (FULL) OF ‘T1‘(Cost=2 Card=1 Bytes=26)

因为x为char类似,在这里oracle把x=001做了隐式转换to_number(x)=001,建在该字段的索引将不起作用,基于函数的索引(function based index)可以在此派上用场,相对于普通索引,fbi是把经过函数转换后的值存放到索引中

SQL> create index t1_fbi on t1(to_number(x));

SQL> analyze table t1 compute statistics for table for all indexes;

SQL> Select * from t1 where x=001;

X     Y         COMM

------ ---------- ----------------------------

001   xxxxx     88888

Execution Plan

----------------------------------------------------------

0     SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1 Bytes=26)

1   0  TABLE ACCESS (BY INDEX ROWID) OF ‘T1‘ (Cost=1 Card=1 Bytes

=26)

2   1    INDEX (RANGE SCAN) OF ‘T1_FBI‘(NON-UNIQUE) (Cost=1 Card

=1)

四:统计信息不是最新的,导致无法使用

五:组合索引中没有用到前导列导致没有用索引,如组合索引(x,y),where条件类似where y=….,此时不走索引(如果x的不同值很少,那么oracle9i以后就有可能走index skip scan,其原理类似于select * from t where y=…and x=(某个确定的值) union all select * from t where y=…and x=(某个确定的值)……..

六:访问的数据比例超过一定范围,优化器会认为full table scan的成本更低,此事走索引扫描反而会使总成本变大,因此,索引用来快速访问表中的少量记录,对于访问表中的大量记录是不适合用索引的。

时间: 2024-10-10 05:57:51

如何导致全表扫描原因的相关文章

如何优雅的使用 参数 is null而不导致全表扫描(破坏索引)

相信大家在很多实际业务中(特别是后台系统)会使用到各种筛选条件来筛选结果集 首先添加测试数据 CREATE TABLE TempList(Id int IDENTITY,Name VARCHAR(12), Age INT) go CREATE INDEX idx_age ON TempList (Age) GO DECLARE @i INT; SET @i=0; WHILE @i<10000 BEGIN INSERT INTO TempList (Name, Age)VALUES(CAST(@i

SQL 数据优化索引建suo避免全表扫描

首先什么是全表扫描和索引扫描?全表扫描所有数据过一遍才能显示数据结果,索引扫描就是索引,只需要扫描一部分数据就可以得到结果.如果数据没建立索引. 无索引的情况下搜索数据的速度和占用内存就会比用索引的检索慢和高.下面是一个例子 1:无索引的情况 Product表,里面没有任何索引,如下图: 从上图中,我悲剧的看到了,物理读是9次,也就说明走了9次硬盘,你也可以想到,走硬盘的目的是为了拿数据,逻辑读有1636次,要注意的是这里 的”次“是“页”的意思,也就是在内存中走了1636个数据页,我用dbcc

【大数据课堂0008】会引起全表扫描的几种SQL 以及sql优化

查询语句的时候尽量避免全表扫描,使用全扫描,索引扫描!会引起全表扫描的几种SQL如下 1.模糊查询效率很低: 原因:like本身效率就比较低,应该尽量避免查询条件使用like:对于like ‘%...%’(全模糊)这样的条件,是无法使用索引的,全表扫描自然效率很低:另外,由于匹配算法的关系,模糊查询的字段长度越大,模糊查询效率越低. 解决办法:首先尽量避免模糊查询,如果因为业务需要一定要使用模糊查询,则至少保证不要使用全模糊查询,对于右模糊查询,即like ‘…%’,是会使用索引的:左模糊lik

MySql查询优化limit 1避免全表扫描(转)

在某些情况下,如果明知道查询结果只有一个,SQL语句中使用LIMIT 1会提高查询效率. 例如下面的用户表(主键id,邮箱,密码): create table t_user(id int primary key auto_increment,email varchar(255),password varchar(255)); 每个用户的email是唯一的,如果用户使用email作为用户名登陆的话,就需要查询出email对应的一条记录. SELECT * FROM t_user WHERE ema

避免全表扫描的sql优化

对查询进行优化,应尽量避免全表扫描,首先应考虑在where 及order by 涉及的列上建立索引:  .尝试下面的技巧以避免优化器错选了表扫描: ·   使用ANALYZE TABLE tbl_name为扫描的表更新关键字分布. ·   对扫描的表使用FORCE INDEX告知MySQL,相对于使用给定的索引表扫描将非常耗时.            SELECT * FROM t1, t2 FORCE INDEX (index_for_column)             WHERE t1.

SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析

原文:SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析 在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index Seek))呢?是否所有情况都是如此?又该如何优化呢? 下面我们通过一些简单的例子来分析理解这些现象.下面的实验环境为SQL SERVER 2008,如果在不同版本有所区别,欢迎指正. 堆表单索引 首先我们构建我们测试需要实验环境,

【翻译自mos文章】SYS_OP_C2C 导致的全表扫描(fts)/全索引扫描

SYS_OP_C2C 导致的全表扫描(fts)/全索引扫描 参考原文: SYS_OP_C2C Causing Full Table/Index Scans (Doc ID 732666.1) 适用于: Oracle Database - Enterprise Edition - Version 10.1.0.2 to 12.1.0.1 [Release 10.1 to 12.1] Information in this document applies to any platform. This

造成MySQL全表扫描的原因

全表扫描是数据库搜寻表的每一条记录的过程,直到所有符合给定条件的记录返回为止.通常在数据库中,对无索引的表进行查询一般称为全表扫描:然而有时候我们即便添加了索引,但当我们的SQL语句写的不合理的时候也会造成全表扫描.以下是经常会造成全表扫描的SQL语句及应对措施: 1. 使用null做为判断条件 如:select account from member where nickname = null; 建议在设计字段时尽量将字段的默认值设为0,改为select account where nickn

执行计划-数据访问方式(全表扫描与4种索引的方式)

执行计划 Oracle执行计划的相关概念: Rowid:系统给oracle数据的每行附加的一个伪列,包含数据表名称,数据库id,存储数据库id以及一个流水号等信息,rowid在行的生命周期内唯一. Recursive sql:为了执行用户语句,系统附加执行的额外操作语句,譬如对数据字典的维护等. Row source(行源):oracle执行步骤过程中,由上一个操作返回的符合条件的行的集合. Predicate(谓词):where后的限制条件. Driving table(驱动表):又称为连接的