索引使用简介
一、 关于索引的知识要写出运行效率高的sql,需要对索引的机制有一定了解,下面对索引的基本知识做一介绍。
1、 索引的优点和局限
索引可以提高查询的效率,但会降低dml操作的效率。
所以建立索引时需要权衡。对于dml操作比较频繁的表,索引的个数不宜太多。
2、 什么样的列需要建索引?
经常用于查询、排序和分组的列(即经常在where、order或group by子句中出现的列)。
3、 主键索引和复合索引
对于一张表的主键,系统会自动为其建立索引。
如果一张表的几列经常同时作为查询条件,可为其建立复合索引。
4、 建立索引的语句
create index i_staff on staff (empno);
create index i_agent on agent (empno, start_date);
5、 删除索引的语句
drop index I_staff;
drop index I_agent;
6、 查询索引的语句
法一:利用数据字典
表一:all_indexes 查看一张表有哪些索引以及索引状态是否有效
主要字段: index_name, table_name, status
例如:select index_name, status
from all_indexes
where table_name=’STAFF_INFO’;
INDEX_NAME STATUS
--------------------- -----------
I_STAFF VALID
表二:all_ind_columns 查看一张表在哪些字段上建了索引
主要字段: table_name, index_name, column_name, column_position
例如: select index_name, column_name, column_position
from all_ind_columns
where table_name=’AGENT’
INDEX_NAME COLUMN_NAME COLUMN_POSITON
--------------------- ----------------------- --------------------------
I_AGENT EMPNO 1
I_AGENT START_DATE 2
由此可见,agent表中有一个复合索引(empno, start_date )
法二:利用toad工具
toad用户界面比sql*plus友好,并且功能强大。你可以在toad编辑器中键入表名,按F4,便可见到这张表的表结构以及所有索引列等基本信息。
7、 索引的一些特点
1): 不同值较多的列上可建立检索,不同值少的列上则不要建。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就没必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
2): 如果在索引列上加表达式,则索引不能正常使用
例如:b1,c1分别是表b,c的索引列
select * from b where b1/30< 1000 ;
select * from c where to_char(c1,’YYYYMMDD HH24:MI:SS’) = ‘200203 14:01:01’;
以上都是不正确的写法
3): where子句中如果使用in、or、like、!=,均会导致索引不能正常使用
例如:select * from b where b1=30 or b1=40;
4): 使用复合索引进行查询时必须使用前置列
例如表a上有一个复合索引(c1,c2,c3),则c1为其前置列
如果用c1或c1+c2或c1+c2+c3为条件进行查询,则该复合索引可以发挥作用,反之,用c2或c3或c2+c3进行查询,则该索引不能起作用。
二. 书写sql注意事项:
1、 避免给sql语句中引用的索引列添加表达式:
典型实例:
b1,c1分别是表b,c的索引列:
1) select * from b where b1/30< 1000 ;
2) select * from c where to_char(c1,’YYYYMMDD HH24:MI:SS’) = ‘200203 14:01:01’;
替代方案:
1) select * from b where b1 < 30000;
2) select * from c where c1 = to_date(‘20020301 14:01:01’, ‘YYYYMMDD HH24:MI:SS’);
注:在lbs中有两个重要字段,pol_info中的undwrt_date和prem_info中的payment_date,这两个日期是带时分秒的,所以经常有同事用to_char 来查询某一时间段的数据。
例如:select count(*) from pol_info where to_char(undwrt_date,’YYYYMMDD’)=’20020416’;
select count(*) from prem_info where to_char(undwrt_date,’YYYYMM’)=’200203’;
替代方案:
select count(*) from pol_info
where undwrt_date>=to_date(’20020416’,’YYYYMMDD’) and
undwrt_date<to_date(’20020417’,’YYYYMMDD’);
select count(*) from prem_info
where payment_date>=to_date(’20020301’,’YYYYMMDD’) and
payment_date<to_date(’20020401’,’YYYYMMDD’); 2、 避免在where子句中使用in、or、like、!=
典型实例:
a1是a表上的索引列:
1) select * from a
where ( a1 = ‘0’ and ...) or (a1 = ‘1’ and ...);
2) select count(*) from a where a1 in (‘0’,’1’) ;
替代方案:
1) select * from a where a1 = ‘0’ and ...
union
select * from a where a1 = ‘1’ and ...
2) select count(*) from a where a1 = ‘0’;
select count(*) from a where a1 = ‘1’;
然后做一次加法运算;或者直接用存储过程来实现;
小结:
对字段使用了 ‘in,or,like’ 做条件、对字段使用了不等号 ‘!=’,均会使索引失效;如果不产生大量重复值,可以考虑把子句拆开;拆开的子句中应该包含索引,或者使用union连结符代替。另一种方式是使用存储过程,它使SQL变得更加灵活和高效。
3、 建立适当的索引
曾经接过开发的一个统计sql, select … from tablea where cola=… and …
运行效率非常慢,经查tablea数据量巨大,再查all_ind_columns,发现cola是tablea的一个复合索引中的一列,但不是前置列。象这种情况,就需要与开发商量,是否针对cola建一个索引。
4、 like和substr
对于‘like’和‘substr’,其效率并没有多大分别。但是,当所搜索的值不存在时,使用‘like’的速度明显大于‘substr’。
所以:select * from a where substr(a1,1,4) = '5378' 可以用like替代
select * from a where a1 like ‘5378%’;
5、 写where条件时,有索引字段的判断在前,其它字段的判断在后;如果where条件中用到复合索引,按照索引列在复合索引中出现的顺序来依次写where条件;
6、使用多表连接时,在from子句中,将记录数少的表放在后面,可提高执行效率;
7、避免使用not in
not in 是效率极低的写法,尽量使用minus或外连接加以替代
典型实例:
1) select col1 from tab1 where col1 not in (select col1 from tab2);
2) select sum(col2) from tab1 where col1 not in (select col1 from tab2);
替代方案
select col1 from tab1 minus select col1 from tab2;
select sum(a.col2) from tab1 a, tab2 b
where a.col1=b.col2(+) and b.col1 is null;
8、多表查询时,如果其中一个表的记录数量明显大于其他表,则可以先对此表进行查询后,再与其他小表进行表连接。
典型实例:
select a.plan_code, b.dno, c,tno, sum(a.tot_modal_prem),
from prem_info a, dept_ref b, plan_type c
where substr(a.deptno,1,7) = substr(b.deptno,1,7)
and a.plan_code = c.plan_code
group by b.dno, c.tno, a.plan_code;
替代方案:
select b.dno, c.tno, a.plan_code, a.tot_amount
from (select plan_code, deptno, sum(tot_modal_prem) tot_amount
from prem_info
group by deptno, plan_code) a
dept_ref b,
plan_type c
where substr(a.deptno,1,7) = substr(b.deptno,1,7)
and a.plan_code = c.plan_code
group by b.dno, c.tno, a.plan_code;
小结:
由于prem_info表的记录数远远大于dept_ref表和plan_type表中的记录数, 所以首先从prem_info表中查询需要的记录,此时记录数已经被大量缩小,然后再和其他两个表连接,速度会得到很大改善! 9、查询数量较大时,使用表连接代替IN,EXISTS,NOT IN,NOT EXISTS等。
典型实例:
a、使用IN:
select sum(col2) from tab1 where col1 in (select col1 from tab2);
使用EXISTS::
select sum(col2) from tab1 a
where exists ( select * from tab2 where col1=a.col1);
b、使用NOT IN:
select sum(col2) from tab1 where col1 not in (select col1 from tab2);
使用NOT EXISTS:
select sum(col2) from tab1 a
where not exists ( select * from tab2 where col1=a.col1);
替代方案:
a、使用连接:
select sum(a.col2) from tab1 a,tab2 b where a.col1=b.col2;
b、使用外连接:
select sum(a.col2) from tab1 a,tab2 b
where a.col1=b.col2(+) and b.col1 is null; 7、 索引的一些特点
1): 不同值较多的列上可建立检索,不同值少的列上则不要建。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就没必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
对于以上的说法,我不以为然。不同值较少的列上如果建立位图索引对于查询是很有好处的(默认的是B 树索引). 对于2
对字段使用了 ‘in,or,like’ 做条件、对字段使用了不等号 ‘!=’,均会使索引失效
-----------
如果用in,ORACLE也可能会利用上索引的,如
select * from tab1 where title in ('aa','bb') 如果在title上建立索引,则oracle在内部可能会执行以下操作
select * from tab1 where title ='aa' union select * from tab2 where title='bb'
从而利用上索引 SQL>select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
PL/SQL Release 9.2.0.4.0 - Production
CORE 9.2.0.3.0 Production
TNS for 32-bit Windows: Version 9.2.0.4.0 - Production
NLSRTL Version 9.2.0.4.0 - Production引用:
1): 不同值较多的列上可建立检索,不同值少的列上则不要建。
比如在雇员表的“性别”列上只有“男”与“女”两个不同值,
因此就没必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
如龙兰花所说这种情况比较适合建立bitmap index.引用:
2): 如果在索引列上加表达式,则索引不能正常使用
例如:b1,c1分别是表b,c的索引列
select * from b where b1/30< 1000 ;
select * from c where to_char(c1,’YYYYMMDD HH24:MI:SS’) = ‘200203 14:01:01’;
以上都是不正确的写法
可以使用函数索引。
SQL>create table b as select rownum rn,t.* from all_tables t;
表已创建。
SQL>create index idx_f_rn on b(rn/30);
索引已创建。
SQL>exec dbms_stats.gather_table_stats('LIZH','B',-
> method_opt=> 'for all indexed columns', cascade=>true );
PL/SQL 过程已成功完成。
SQL>select count(*) from b;
COUNT(*)
----------
1262
SQL>select * from b where rn/30<0.5;
已选择14行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=15 Bytes=1500)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=3 Card=15 Bytes=1500)
2 1 INDEX (RANGE SCAN) OF 'IDX_F_RN' (NON-UNIQUE) (Cost=2 Card=15)
说明:对于我这里因为数据量太小所以使用rn/30<0.5,如果rn/30<的数字比较大 因为选择的行占的比例较大
则不会使用索引,也不应该使用索引:(具体的比例跟数据的聚集程度有关系)
SQL>select * from b where rn/30<20;
已选择599行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=594 Bytes=59400)
1 0 TABLE ACCESS (FULL) OF 'B' (Cost=5 Card=594 Bytes=59400)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
75 consistent gets
0 physical reads
0 redo size
27626 bytes sent via SQL*Net to client
932 bytes received via SQL*Net from client
41 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
599 rows processed
SQL>select/*+ index(b idx_f_rn)*/ * from b where rn/30<20;
已选择599行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=20 Card=594 Bytes=59400)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=20 Card=594 Bytes=59400)
2 1 INDEX (RANGE SCAN) OF 'IDX_F_RN' (NON-UNIQUE) (Cost=4 Card=594)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
98 consistent gets
0 physical reads
0 redo size
27626 bytes sent via SQL*Net to client
932 bytes received via SQL*Net from client
41 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
599 rows processed
可以看到使用索引的时候consistent gets大于不使用索引引用:
3): where子句中如果使用in、or、like、!=,均会导致索引不能正常使用
例如:select * from b where b1=30 or b1=40;
不同意:
SQL>create index idx_table_name on b(table_name);
索引已创建。
SQL>exec dbms_stats.gather_table_stats('LIZH','B',-
> method_opt=> 'for all indexed columns', cascade=>true );
PL/SQL 过程已成功完成。
SQL>select * from b where table_name ='MID_TABLE' OR TABLE_NAME='MID_TABLE2';
已选择2行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=2 Bytes=200)
1 0 INLIST ITERATOR
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=3 Card=2 Bytes=200)
3 2 INDEX (RANGE SCAN) OF 'IDX_TABLE_NAME' (NON-UNIQUE) (Cost=2 Card=2)
SQL>select * from b where table_name IN('MID_TABLE' ,'MID_TABLE2');
已选择2行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=2 Bytes=200)
1 0 INLIST ITERATOR
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=3 Card=2 Bytes=200)
3 2 INDEX (RANGE SCAN) OF 'IDX_TABLE_NAME' (NON-UNIQUE) (Cost=2 Card=2)
可以看到这两种写法执行计划完全一样,而且使用索引。
LIKE:
SQL>ALTER SESSION SET OPTIMIZER_MODE=FIRST_ROWS;
会话已更改。
SQL>SELECT * FROM B WHERE TABLE_NAME LIKE 'MID%';
已选择3行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=68 Card=159 Bytes=15900)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=68 Card=159 Bytes=15900)
2 1 INDEX (RANGE SCAN) OF 'IDX_TABLE_NAME' (NON-UNIQUE) (Cost=2 Card=159)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
3398 bytes sent via SQL*Net to client
503 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3 rows processed
SQL>ALTER SESSION SET Optimizer_MODE=CHOOSE;
会话已更改。
SQL>SELECT * FROM B WHERE TABLE_NAME LIKE 'MID%';
已选择3行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=159 Bytes=15900)
1 0 TABLE ACCESS (FULL) OF 'B' (Cost=5 Card=159 Bytes=15900)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
37 consistent gets
0 physical reads
0 redo size
3398 bytes sent via SQL*Net to client
503 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3 rows processed
至于!= 因为可能涉及的行的比例比较大所以不使用索引。引用:
4): 使用复合索引进行查询时必须使用前置列
例如表a上有一个复合索引(c1,c2,c3),则c1为其前置列
如果用c1或c1+c2或c1+c2+c3为条件进行查询,则该复合索引可以发挥作用,反之,
用c2或c3或c2+c3进行查询,则该索引不能起作用。
9i以前是这样的,不过9i中有了SKIP SCAN 他主要适用于前缀字段的值比较少比如:性别 。
SQL>create table t
2 as
3 select decode(mod(rownum,2), 0, 'M', 'F' ) gender, all_objects.*
4 from all_objects
5 ;
表已创建。
SQL>CREATE INDEX IDX_T ON T(GENDER,OBJECT_ID);
索引已创建。
SQL>analyze table T compute statistics
2 for table
3 for all indexes
4 for all indexed columns;
表已分析。
SQL>select * from t t11 where object_id = 100;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1 Bytes=98)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=4 Card=1 Bytes=98)
2 1 INDEX (SKIP SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=3 Card=1)
对于前缀接近唯一的情况则不会使用索引。
SQL>DROP INDEX IDX_TABLE_NAME;
索引已丢弃。
SQL>CREATE INDEX IDX_COMBIDX ON B(RN,TABLE_NAME);
索引已创建。
SQL>exec dbms_stats.gather_table_stats('LIZH','B',-
> method_opt=> 'for all indexed columns', cascade=>true );
PL/SQL 过程已成功完成。
SQL>SELECT * FROM B B99 WHERE TABLE_NAME='ALL_OBJECTS';
未选定行
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=1 Bytes=182)
1 0 TABLE ACCESS (FULL) OF 'B' (Cost=5 Card=1 Bytes=182)引用:
4、 like和substr
对于‘like’和‘substr’,其效率并没有多大分别。但是,当所搜索的值不存在时,使用‘like’的速度明显大于‘substr’。
所以:select * from a where substr(a1,1,4) = '5378' 可以用like替代
select * from a where a1 like ‘5378%’;
不同意:
SQL>alter session set optimizer_mode=first_rows;
会话已更改。
对于非函数索引:
SQL>create index idx_table_name on b(table_name);
索引已创建。
SQL>exec dbms_stats.gather_table_stats('LIZH','B',-
> method_opt=> 'for all indexed columns', cascade=>true );
PL/SQL 过程已成功完成。
SQL>select * from b where table_name like 'B%';
已选择11行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=10 Card=17 Bytes=3094)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=10 Card=17 Bytes=3094)
2 1 INDEX (RANGE SCAN) OF 'IDX_TABLE_NAME' (NON-UNIQUE) (Cost=2 Card=17)
SQL>select * from b where substr(table_name,1,1)='B';
已选择11行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=5 Card=13 Bytes=2366)
1 0 TABLE ACCESS (FULL) OF 'B' (Cost=5 Card=13 Bytes=2366)引用:
5、 写where条件时,有索引字段的判断在前,其它字段的判断在后;
如果where条件中用到复合索引,按照索引列在复合索引中出现的顺序来依次写where条件;
顺序没有关系:
SQL>rop index idx_table_name;
索引已丢弃。
SQL>select * from b where table_name='MID_TABLE' AND RN=1;
未选定行
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=182)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=2 Card=1 Bytes=182)
2 1 INDEX (RANGE SCAN) OF 'IDX_COMBIDX' (NON-UNIQUE) (Cost=1 Card=1)引用:
6、使用多表连接时,在from子句中,将记录数少的表放在后面,可提高执行效率;CBO中没有这个限制:
SQL>SELECT * FROM T,B
2 WHERE T.OBJECT_NAME=B.TABLE_NAME
3 AND B.RN=1;
未选定行
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=7 Card=1 Bytes=287)
1 0 HASH JOIN (Cost=7 Card=1 Bytes=287)
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=3 Card=1 Bytes=182)
3 2 INDEX (RANGE SCAN) OF 'IDX_COMBIDX' (NON-UNIQUE) (Cost=2 Card=1)
4 1 TABLE ACCESS (FULL) OF 'T' (Cost=3 Card=1000 Bytes=105000)
SQL>SELECT * FROM B,T
2 WHERE T.OBJECT_NAME=B.TABLE_NAME
3 AND B.RN=1;
未选定行
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=7 Card=1 Bytes=287)
1 0 HASH JOIN (Cost=7 Card=1 Bytes=287)
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'B' (Cost=3 Card=1 Bytes=182)
3 2 INDEX (RANGE SCAN) OF 'IDX_COMBIDX' (NON-UNIQUE) (Cost=2 Card=1)
4 1 TABLE ACCESS (FULL) OF 'T' (Cost=3 Card=1000 Bytes=105000)
执行计划没有任何区别。
(注释:我测试了把统计信息删除、ALTER SESSION SET OPTIMIZER_MODE=RULE、提示RULE等方法 发现两个表在FROM中的
位置不同但是执行计划也相同
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'B'
2 1 NESTED LOOPS
3 2 TABLE ACCESS (FULL) OF 'T'
4 2 INDEX (RANGE SCAN) OF 'IDX_COMBIDX' (NON-UNIQUE)
)引用:
7、避免使用not in
not in 是效率极低的写法,尽量使用minus或外连接加以替代
典型实例:
1) select col1 from tab1 where col1 not in (select col1 from tab2);
2) select sum(col2) from tab1 where col1 not in (select col1 from tab2);
替代方案
select col1 from tab1 minus select col1 from tab2;
select sum(a.col2) from tab1 a, tab2 b
where a.col1=b.col2(+) and b.col1 is null;
不同意:
SQL>CREATE TABLE T AS SELECT * FROM DBA_OBJECTS WHERE OBJECT_TYPE<>'DATABASE LINK';
表已创建。
SQL>CREATE TABLE T1 AS SELECT * FROM DBA_OBJECTS WHERE ROWNUM<=31500;
表已创建。
SQL>ALTER TABLE T MODIFY OBJECT_ID NOT NULL;
表已更改。
SQL>ALTER TABLE T1 MODIFY OBJECT_ID NOT NULL;
表已更改。
SQL>CREATE INDEX IDX_T_OBJECTID ON T(OBJECT_ID);
索引已创建。
SQL>CREATE INDEX IDX_T1_OBJECTID ON T1(OBJECT_ID);
索引已创建。
SQL>exec dbms_stats.gather_table_stats('LIZH','T',-
> method_opt=> 'for all indexed columns', cascade=>true );
PL/SQL 过程已成功完成。
SQL>exec dbms_stats.gather_table_stats('LIZH','T1',-
> method_opt=> 'for all indexed columns', cascade=>true );
PL/SQL 过程已成功完成。
SQL>SELECT T.*
2 FROM T LEFT JOIN T1
3 ON T.OBJECT_ID=T1.OBJECT_ID
4 WHERE T1.OBJECT_ID IS NULL;
已选择42行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=114 Card=31541 Bytes=3311805)
1 0 FILTER
2 1 HASH JOIN (OUTER)
3 2 TABLE ACCESS (FULL) OF 'T' (Cost=43 Card=31541 Bytes=3154100)
4 2 INDEX (FAST FULL SCAN) OF 'IDX_T1_OBJECTID' (NON-UNIQUE) (Cost=8 Card=3150
0 Bytes=157500)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
508 consistent gets
518 physical reads
0 redo size
4462 bytes sent via SQL*Net to client
525 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
42 rows processed
SQL>select * from T where OBJECT_ID not in
2 (select OBJECT_ID from T1);
已选择42行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=114 Card=41 Bytes=4305)
1 0 HASH JOIN (ANTI) (Cost=114 Card=41 Bytes=4305)
2 1 TABLE ACCESS (FULL) OF 'T' (Cost=43 Card=31541 Bytes=3154100)
3 1 INDEX (FAST FULL SCAN) OF 'IDX_T1_OBJECTID' (NON-UNIQUE) (Cost=8 Card=31500
Bytes=157500)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
508 consistent gets
518 physical reads
0 redo size
4462 bytes sent via SQL*Net to client
525 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
42 rows processed
执行计划虽然有区别,但是效果差不多。
不过要注意 如果T T1中有一个OBJECT_ID 没有NOT NULL 约束那么NOT IN 在语义上是有问题的,
此时NOT IN 的性能也会极差。
总结:
SQL是否应用索引通过观察执行计划就能够得到判断。
SQL是否应该使用索引则是在调试SQL的过程中多比较各种SQL的执行计划、统计信息最终确定的
(不过开发的过程中要注意你的环境和上线的运行环境中是存在差异的,应该尽量建立一个接近上线的运行环境的测试环境)
然后不断的积累经验,并且注意不能一概而论。
页:
[1]