用合适的索引避免不必要的全表扫描

Oracle数据库里大部分SQL优化的问题都可以增加或减少索引的方式来解决,但这绝不是全部。当目标SQL语句所要查询的只是目标表中的一部分数据时,通过创建合适的索引就能够避免在没有索引的情况下为查询这一小部分数据而不得不采用全表扫描的操作,这样就降低了目标SQL语句的资源消耗,同时也会缩短了执行时间。

创建一张测试表及创建一个普通的单键值B树索引:

SQL> create table t1 as select * from dba_objects;

Table created.

SQL> create index idx_t1 on t1(object_id);

Index created.

清空缓存数据,我们看下一个SQL查询的执行计划及资源消耗:

SQL> alter system flush buffer_cache;

System altered.

SQL> set timing on
SQL> set autotrace traceonly
SQL> select * from t1 where object_id is null;

no rows selected

Elapsed: 00:00:00.11

Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    12 |  2484 |   291   (1)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T1   |    12 |  2484 |   291   (1)| 00:00:04 |
--------------------------------------------------------------------------

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

   1 - filter("OBJECT_ID" IS NULL)

Note
-----
   - dynamic sampling used for this statement (level=2)

Statistics
----------------------------------------------------------
        308  recursive calls
          0  db block gets
       1151  consistent gets
     1038  physical reads
          0  redo size
       1183  bytes sent via SQL*Net to client
        405  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

从上面的查询中可以看到,SQL语句查询走的全表扫描,有1151个一致性读,1038个物理读。

为什么没有走索引哪?

原来对于普通的单键值B树索引而言,NULL值不入索引的,所以即便在OBJECT_ID上有单键值B树索引IDX_T1,在执行上面的查询时也用不上。对于上面的SQL有没有办法让其走索引那?如果你对Oracle数据库里B树索引的结构和原理都很了解的话就不难回答这个问题。

这里只需建立一个复合B树索引就可以了。

删除原来的IDX_T1索引,重新创建一个同名的复合B树索引IDX_T1,其前导列依然是object_id,但第二列是一个常数0,这里利用的原理是---虽然对于单键值B树索引而言NULL值不入索引,但对于复合B树索引来说,NULL值是入索引的。

SQL> drop index idx_t1;

Index dropped.
SQL> create index idx_t1 on t1(object_id,0);

Index created

重新执行同样的SQL查询:

SQL> alter system flush buffer_cache;

System altered.

Elapsed: 00:00:00.11
SQL>
SQL> select * from t1 where object_id is null;

no rows selected

Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
Plan hash value: 50753647

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |    12 |  2484 |    97   (0)| 00:00:02 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1     |    12 |  2484 |    97   (0)| 00:00:02 |
|*  2 |   INDEX RANGE SCAN          | IDX_T1 |  4314 |       |    11   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

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

   2 - access("OBJECT_ID" IS NULL)

Note
-----
   - dynamic sampling used for this statement (level=2)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          2  consistent gets
          2  physical reads
          0  redo size
       1183  bytes sent via SQL*Net to client
        405  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

创建复合索引重新执行后可以看到Oracle这次查询就用到了B树复合索引IDX_T1,数据从全表扫描变成了索引范围扫描,数据耗费时间、一致性读和物理读也大幅度下降了。
     通过创建合适的索引来避免不必要的全表扫描,大幅降低了目标SQL 语句的资源消耗,进而大幅度的缩短了SQL的执行时间。

--摘自崔华基于Oracle的SQL优化

时间: 2024-10-15 13:04:10

用合适的索引避免不必要的全表扫描的相关文章

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

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

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

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

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

oracle 性能优化操作十八: 决定使用全表扫描还是使用索引

和所有的秘笈一样,最后一招都会又回到起点,最后我们来讨论一下是否需要建立索引,也许进行全表扫描更快. 在大多数情况下,全表扫描可能会导致更多的物理磁盘输入输出,但是全表扫描有时又可能会因为高度并行化的存在而执行的更快. 如果查询的表完全没有顺序,那么一个要返回记录数小于10%的查询可能会读取表中大部分的数据块,这样使用索引会使查询效率提高很多. 但是如果表非常有顺序,那么如果查询的记录数大于40%时,可能使用全表扫描更快. 因此,有一个索引范围扫描的总体原则是: 1)对于原始排序的表  仅读取少

【翻译自mos文章】使用索引快速全扫(index ffs) 来避免全表扫描

使用索引快速全扫(index ffs) 来避免全表扫描 参考原文: Index Fast Full Scan Usage To Avoid Full Table Scans (Doc ID 70135.1) 适用于: Oracle Database - Enterprise Edition - Version 7.3.0.0 to 11.2.0.3 [Release 7.3.0 to 11.2] Information in this document applies to any platfo

如何优雅的使用 参数 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,对其进行优化走索引,并且将执行计划稳定到baseLine。

①创建表t3: SQL> create table t3 (id int); Table created. SQL> insert into t3 select level from dual connect by level<=100000; 100000 rows created. ②开启自动捕获并修改时间格式: SQL> alter system set optimizer_capture_sql_plan_baselines=true; System altered. SQ

oracle在组合索引上,只使用部分列进行查询(查询时必须包含前导列,否则会走全表扫描)

实验环境:Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production 1.创建表插入数据 SQL> create table txtx(id int,name char(2),tx char(3),id1 int,primary key(id,name,tx)); 表已创建. SQL> insert into txtx values(1,'tx','tx',1); 已创建 1 行. SQL> i