您的位置:首页 > 数据库

给数据库建索引的规则

2015-11-24 15:05 190 查看
给数据库建索引的规则


建立索引常用的规则如下:

1、表的主键、外键必须有索引;

2、数据量超过300的表应该有索引;

3、经常与其他表进行连接的表,在连接字段上应该建立索引;

4、经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;

5、索引应该建在选择性高的字段上;

6、索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;

7、复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:

A、正确选择复合索引中的主列字段,一般是选择性较好的字段;

B、复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;

C、如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;

D、如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;

E、如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;

8、频繁进行数据操作的表,不要建立太多的索引;

9、删除无用的索引,避免对执行计划造成负面影响;

以上是一些普遍的建立索引时的判断依据。一言以蔽之,索引的建立必须慎重,对每个索引的必要性都应该经过仔细分析,要有建立的依据。因为太多的索引与不充分、不正确的索引对性能都毫无益处:在表上建立的每个索引都会增加存储开销,索引对于插入、删除、更新操作也会增加处理上的开销。另外,过多的复合索引, 在有单字段索引的情况下,一般都是没有存在价值的;相反,还会降低数据增加删除时的性能,特别是对频繁更新的表来说,负面影响更大

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

ORACLE查询的优化及索引

申明:

下面内容来自http://www.delphibbs.com/keylife/iblog_show.asp?xid=9256



1.合理使用索引

索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:

●经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。

●频繁进行排序或分组(即进行group by或order by操作)的列上建立索引

●条件表达式中经常用到的不同值较多的列建立检索,不同值少的列不要建立索引。比如雇员表的“性别”列只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。

●如果待排序的列有多个,可以这些列建立复合索引(compoundindex)。

●使用系统工具。如Informix数据库有一个tbcheck工具,可以可疑的索引进行检查。一些数据库服务器,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。



(1)下面两条select语句中:

 select * from table1 wherefield1<=10000 and field1>=0;

 select * from table1 wherefield1>=0 and field1<=10000;

 如果数据表中的数据field1都>=0,则第一条select语句要比第二条select语句效率高的多,因为第二条select语句的第一个条件耗费了大量的系统资源。

 第一个原则:在where子句中应把最具限制性的条件放最前面。



(2)下面的select语句中:

 select * from tab wherea=… and b=… and c=…;

 若有索引index(a,b,c),则where子句中字段的顺序应和索引中字段顺序一致。

 第二个原则:where子句中字段的顺序应和索引中字段顺序一致。



以下假设field1有唯一索引I1,field2有非唯一索引I2。

(3) selectfield3,field4 from tb wherefield1='sdf' 快

 select * from tb wherefield1='sdf' 慢,

因为后者索引扫描后要多一步ROWID表访问。



(4) selectfield3,field4 from tb wherefield1>='sdf' 快

select field3,field4 from tb wherefield1>'sdf' 慢

因为前者可以迅速定位索引。



(5) selectfield3,field4 from tb wherefield2 like 'R%' 快

 select field3,field4 from tb wherefield2 like '%R' 慢,

 因为后者不使用索引。



(6) 使用函数如:

select field3,field4 from tb whereupper(field2)='RMN'不使用索引。

如果一个表有两万条记录,建议不使用函数;如果一个表有五万条以记录,严格禁止使用函数!两万条记录以下没有限制。



(7) 空值不索引中存储,所以

 select field3,field4 from tb wherefield2 is[not] null不使用索引。



(8) 不等式如

 select field3,field4 from tb wherefield2!='TOM'不使用索引。

 相似地,

 select field3,field4 from tb wherefield2 not in('M','P')不使用索引。



(9) 多列索引,只有当查询中索引首列被用于条件时,索引才能被使用。



(10) MAX,MIN等函数,如

Select max(field2) from tb使用索引。所以,如果需要对字段取max,min,sum等,应该加索引。

一次只使用一个聚集函数,如:

select “min”=min(field1), “max”=max(field1) from tb

不如:select “min”=(selectmin(field1) from tb) , “max”=(selectmax(field1) from tb)



(11) 重复值过多的索引不会被查询优化器使用。而且因为建了索引,修改该字段值时还要修改索引,所以更新该字段的操作比没有索引更慢。



(12) 索引值过大(如一个char(40)的字段建索引),会造成大量的I/O开销(甚至会超过表扫描的I/O开销)。因此,尽量使用整数索引。 Sp_estspace可以计算表和索引的开销。



(13) 对于多列索引,order by的顺序必须和索引的字段顺序一致。



(14) sybase中,如果orderby的字段组成一个簇索引,那么无须做order by。记录的排列顺序是与簇索引一致的。



(15) 多表联结(具体查询方案需要通过测试得到)

 where子句中限定条件尽量使用相关联的字段,且尽量把相关联的字段放前面。

 select a.field1,b.field2 from a,b where a.field3=b.field3

 1. field3没有索引的情况下:

 对a作全表扫描,结果排序

 对b作全表扫描,结果排序

 结果合并。

 对于很小的表或巨大的表比较合适。



 2. field3有索引

 按照表联结的次序,b为驱动表,a为被驱动表

 对b作全表扫描

 对a作索引范围扫描

 如果匹配,通过a的rowid访问



(16) 避免一对多的join。如:

 select tb1.field3,tb1.field4,tb2.field2 from tb1,tb2
where
tb1.field2=tb2.field2and tb1.field2=‘BU1032’ and tb2.field2= ‘aaa’

 不如:

 declare @a varchar(80)

select @a=field2 from tb2 wherefield2=‘aaa’

 select tb1.field3,tb1.field4,@a from tb1 where field2= ‘aaa’



(16) 子查询

 用exists/not exists代替in/notin操作

 比较:

 select a.field1 from a wherea.field2 in(selectb.field1 from b
whereb.field2=100)

 select a.field1 from a whereexists(
select
1from b wherea.field2=b.field1 and b.field2=100)



 select field1 from a wherefield1 not in(
select
field2 from b)

 select field1 from a wherenot exists(
select
1from b whereb.field2=a.field1)



(17) 主、外键主要用于数据约束,sybase中创建主键时会自动创建索引,外键与索引无关,提高性能必须再建索引。



(18) char类型的字段不建索引比int类型的字段不建索引更糟糕。建索引后性能只稍差一点。



(19) 使用count(*)而不要使用count(column_name),避免使用count(distinctcolumn_name)。



(20) 等号右边尽量不要使用字段名,如:

select * from tb wherefield1 = field3



(21) 避免使用or条件,因为or不使用索引。



2.避免使用order by和group by字句。

 因为使用这两个子句会占用大量的临时空间(tempspace),如果一定要使用,可用视图、人工生成临时表的方法来代替。

 如果必须使用,先检查memory、tempdb的大小。

 测试证明,特别要避免一个查询里既使用join又使用groupby,速度会非常慢!



3.尽量少用子查询,特别是相关子查询。因为这样会导致效率下降。

一个列的标签同时主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要子查询中过滤掉尽可能多的行。







4.消除对大型表行数据的顺序存取

嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要“学号”这个连接字段上建立索引

还可以使用并集来避免顺序存取。尽管所有的检查列都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:

SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001)OR order_num=1008

虽然customer_num和order_num建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:

SELECT * FROM orders WHERE customer_num=104 AND order_num>1001

UNION

SELECT * FROM orders WHERE order_num=1008

这样就能利用索引路径处理查询。

5.避免困难的正规表达式

MATCHES和LIKE关键字支持通配符匹配,技术叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer
WHERE zipcode LIKE “98_ _ _”

即使zipcode字段建立了索引,这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer
WHERE zipcode >“98000”,执行查询时就会利用索引来查询,显然会大大提高速度。

另外,还要避免非开始的子串。例如语句:SELECT * FROMcustomer WHEREzipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。

6.使用临时表加速查询

把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且其他方面还能简化优化器的工作。例如:

SELECT cust.name,rcvbles.balance,……other columns

FROM cust,rcvbles

WHERE cust.customer_id = rcvlbes.customer_id

AND rcvblls.balance>0

AND cust.postcode>“98000”

ORDER BY cust.name

如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放一个临时文件中,并按客户的名字进行排序:

SELECT cust.name,rcvbles.balance,……other columns

FROM cust,rcvbles

WHERE cust.customer_id = rcvlbes.customer_id

AND rcvblls.balance>0

ORDER BY cust.name

INTO TEMP cust_with_balance

然后以下面的方式临时表中查询:

SELECT * FROM cust_with_balance

WHERE postcode>“98000”

临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。

注意:临时表创建后不会反映主表的修改。主表中数据频繁修改的情况下,注意不要丢失数据。

7.用排序来取代非顺序存取

非顺序磁盘存取是最慢的操作,表现磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们写应用程序时很容易写出要求存取大量非顺序页的查询。

有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。

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

ORACLE索引与高性能SQL介绍

什么是索引

  

  索引
是建立在表一列或多个列上辅助对象,目是加快访问表中数据;

  

  Oracle存储索引的数据结构是B*树,位图索引也是如此,只不过是叶子节点不同B*数索引

  

  索引由根节点、分支节点和叶子节点组成,上级索引块包含下级索引的索引数据,叶节点包含索引数据和确定行实际位置rowid。

  

  使用索引的

  加快查询速度

  减少I/O操作

  消除磁盘排序

  

  何时使用索引

  查询返回记录数

  排序表<40%

  非排序表 <7%

  表碎片较多(频繁增加、删除)

  

  索引的种类

  非唯一索引(最常用)

  唯一索引

  位图索引

  局部有前缀分区索引

  局部无前缀分区索引

  全局有前缀分区索引

  散列分区索引

  基于函数的索引

  

  管理索引的准则

  

  在表中插入数据后创建索引

  


  。在用SQL*Loader或import工具插入或装载数据后,建立索引比较有效;

  

  索引正确的表和列

  

  。经常检索排序大表中40%或非排序表7%行,建议建索引

  。为了改善多表关联,索引列用于联结;

  。列中值相对比较唯一;

  。取值范围(大:B*树索引,小:位图索引);

  。Date型列一般适合基于函数的索引

  。列中有许多空值,不适合建立索引

  

  为性能而安排索引列


  

  。经常一起使用多个字段检索记录,组合索引比单索引更有效;

  。把最常用列放在最前面,例:dx_groupid_serv_id(groupid,serv_id),在where条件中使用groupid或groupid,serv_id,查询将使用索引,若仅用到serv_id字段,则索引无效;

  。合并/拆分不必要的索引

  

  限制每个表索引的数量

  

  。一个表可以有几百个索引(你会这样做?),但是对于频繁插入和更新表,索引越多系统CPU,I/O负担就越重;

  。建议每张表不超过5个索引

  

  删除不再需要的索引

  

  。索引无效,集中表现在该使用基于函数的索引或位图索引,而使用了B*树索引

  。应用中查询不使用索引

  。重建索引之前必须先删除索引,若用alter index … rebuild重建索引,则不必删除索引

  

  索引数据块空间使用

  

  。创建索引时指定表空间,特别是在建立主键时,应明确指定表空间;

  。合理设定pctfress,注意:不能给索引指定pctused;

  。估计索引的大小和合理地设置存储参数,默认为表空间大小,或initial与next设置成一样大。

  

  考虑并行创建索引

  

  。对大表可以采用并行创建索引,在并行创建索引时,存储参数被每个查询服务器进程分别使用,例如:initial为1M,并行度为8,则创建索引期间至少要消耗8M空间;

  

  考虑用nologging创建索引

  

  。对大表创建索引可以使用nologging来减少重做日志;

  。节省重做日志文件空间;

  。缩短创建索引的时间;

  。改善了并行创建大索引性能。

  

  怎样建立最佳索引

  

  明确地创建索引

  create indexindex_name on table_name(field_name)

  tablespacetablespace_name

  pctfree 5

  initrans 2

  maxtrans 255

  storage

  (

  minextents 1

  maxextents16382

  pctincrease0

  );

  

  创建基于函数的索引

  

  。常用与UPPER、LOWER、TO_CHAR(date)等函数分类上,例:

  create indexidx_func on emp (UPPER(ename)) tablespace tablespace_name;

  

  创建位图索引

  

  。对基数较小,且基数相对稳定列建立索引时,首先应该考虑位图索引,例:

  createbitmap index idx_bitm on class (classno) tablespace tablespace_name;

  

  明确地创建唯一索引

  

  。可以用createunique index语句来创建唯一索引,例:

  createunique index dept_unique_idx on dept(dept_no) tablespace idx_1;

  

  创建与约束相关的索引

  

  。可以用usingindex字句,为与unique和primary key约束相关的索引,例如:

  alter tabletable_name

  addconstraint PK_primary_keyname primary key (field_name)

  using indextablespace tablespace_name;

  

  如何创建局部分区索引

  

  。基础表必须是分区表;

  。分区数量与基础表相同;

  。每个索引分区子分区数量与相应基础表分区相同;

  。基础表子分区中的索引项,被存储在该索引的相应子分区中,例如:

  Create IndexTG_CDR04_SERV_ID_IDX On TG_CDR04(SERV_ID)

  Pctfree 5

  TablespaceTBS_AK01_IDX

  Storage (

  MaxExtents32768

  PctIncrease0

  FreeLists 1

  FreeListGroups 1

  )

  local

  /

  

  如何创建范围分区的全局索引

  

  。基础表可以是全局表和分区表。

  create indexidx_start_date on tg_cdr01(start_date)

  globalpartition by range(start_date)

  (partitionp01_idx vlaues less than (‘0106’)

  partitionp01_idx vlaues less than (‘0111’)

  …

  partitionp01_idx vlaues less than (‘0401’ ))

  /

  

  重建现存的索引

  

  重建现存的索引的当前时刻不会影响查询;

  

  重建索引可以删除额外数据块;

  

  提高索引查询效率;

  alter indexidx_name rebuild nologging;

  

  对于分区索引

  alter indexidx_name rebuild partition partiton_name nologging;

  

  要删除索引的原因

  

  。不再需要的索引

  。索引没有针对其相关表所发布查询提供所期望性能改善;

  。应用没有用该索引来查询数据;

  。该索引无效,必须在重建之前删除该索引

  。该索引已经变太碎了,必须在重建之前删除该索引

  。语句:dropindex idx_name;drop index idx_name drop partition partition_name;

  

  建立索引的代价

  

  基础表维护时,系统要同时维护索引,不合理的索引将严重影响系统资源,主要表现在CPU和I/O上;

  

  插入、更新、删除数据产生大量db file sequential read锁等待;

  

  SQL优化器简介

  

  基于规则的优化器


  

  。总是使用索引

  。总是从驱动表开始(from子句最右边表)

  。只有在不可避免情况下,才使用全表扫描

  。任何索引都可以

  

  基于成本的优化器

 。需要表、索引的统计资料

  Analyzetable customer compute statistics;

  Analyzetable customer estimate statistics sample 5000 rows;

  。表中设置并行度、表分区

  

  优化器模式

  

  rule模式

  

  。总忽略CBO和统计信息而基于规则

  choose模式

  

  。Oracle根据情况选择rule or first_rows or all_rows

  first_rows 模式

  

  。基于成本,以最快速度返回记录,会造成总体查询速度下降或消耗更多资源,倾向索引扫描,适合OLTP系统

  all_rows模式

  

  。基于成本,确保总体查询时间最短,倾向并行全表扫描

  

  例如:

  Selectlast_name from customer order by last_name;用first_rows时,迅速返回记录,但I/O量大,用all_rows时,返回记录慢,但使用资源少。

  

  调整SQL表访问

  

  全表扫描

  

  。返回记录:未排序表>40%,排序表>7%,建议采用并行机制来提高访问速度,DDS;

  

  索引访问

  

  。最常用方法,包括索引唯一扫描和索引范围扫描,OLTP;

  

  快速完全索引扫描

  

  。访问索引中所有数据块,结果相当于全表扫描,可以用索引扫描代替全表扫描,例如:

  

  Selectserv_id,count(* ) from tg_cdr01 group by serv_id;

  

  评估全表扫描合法性

  

  如何实现并行扫描

  

  。永久并行化(不推荐)

  alter tablecustomer parallel degree 8;

  

  。单个查询并行化

  select /*+full(emp) parallel(emp,8)*/ * from emp;

  

  分区表效果明显

  

  优化SQL语句排序

  

  排序操作:

  

  。order by 子句

  。group by 子句

  。selectdistinct子句

  。创建索引

  。union或minus

  。排序合并连接

  

  如何避免排序

  

  。添加索引

  。在索引中使用distinct子句

  。避免排序合并连接

  

  使用提示进行调整

  

  使用提示原则

  

  。语法:/*+ hint*/

  。使用表别名:select /*+ index(edept_idx)*/ * from emp e

  。检验提示

  

  常用提示

  

  。rule

  。all_rows

  。first_rows

  。use_nl

  。use_hash

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

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

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

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

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

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

Oracle索引

博客分类:

plsql

OracleSQL
Oracle索引(转载)一.B-Tree索引:

1.

选项择索引字段的原则:

l

在WHERE子句中最频繁使用的字段

l

联接语句中的联接字段

l

选择高选择性的字段(如果很少的字段拥有相同值,即有很多独特值,则选择性很好)

l

ORACLE在UNIQUE和主键字段上自动建立索引

l

在选择性很差的字段上建索引只有在这个字段的值分布非常倾斜的情况下下才有益(在这种情况下,某一,两个字段值比其它字值少出现很多)

l

不要在很少独特值的字段上建B-TREE索引,在这种情况下,你可以考虑在这些字段上建位图索引.在联机事务处理环境下,所由并发性非常高,索引经常被修改,所以不应该建位图索引

l

不要在经常被修改的字段上建索引.当有UPDATE,DELETE,INSETT操作时,ORACLE除了要更新表的数据外,同时也要更新索引,而且就象更新数据一样,或产生还原和重做条目

l

不要在有用到函数的字段上建索引,ORACLE在这种情况,优化器不会用到索引,除非你建立函数索引

l

可以考虑在外键字段上建索引,这些索引允许当在主表上UPDATE,DELETE操作时,不需要共享子表的锁,这非常适用于在父表和子表上有很多并发的INSERT,UPDATE和DELETE操作的情况

l

当建立索引后,请比较一下索引后所获得的查询性能的提高和UPDATE,DELETE,INSERT操作性能上的损失,比较得失后,再最后决定是否需建立这个索引

2.

选择建立复合索引

复合索引的优点:

l

改善选择性:复合索引比单个字段的索引更具选择性

l

减少I/O:如果要查询的字段刚好全部包含在复合索引的字段里,则ORACLE只须访问索引,无须访问表

什么情况下优化器会用到复合索引呢?

(a)当SQL语句的WHERE子句中有用到复合索引的领导字段时,ORACLE优化器会考虑用到复合索引来访问.

(b)当某几个字段在SQL语句的WHERE子句中经常通过AND操作符联合在一些使用作为过滤谓词,并且这几个字段合在一起时选择性比各自单个字段的选择性要更好时,可能考虑用这几个字段来建立复合索引.

(c)当有几个查询语句都是查询同样的几个字段值时,则可以考虑在这几个字段上建立复合索引.

复合索引字段排序的原则:

l

确保在WHERE子句中使用到的字段是复合索引的领导字段

l

如果某个字段在WHERE子句中最频繁使用,则在建立复合索引时,考虑把这个字段排在第一位(在CREATEINDEX语句中)

l

如果所有的字段在WHERE子句中使用频率相同,则将最具选择性的字段排在最前面,将最不具选择性的字段排在最后面

l

如果所有的字段在WHERE子句中使用频率相同,如果数据在物理上是按某一个字段排序的,则考虑将这个字段放在复合索引的第一位

二.位图索引

什么情况下位图索引能够改善查询的性能呢?

l

WHERE子句包含多个谓词于中低基数的字段

l

单个的谓词在这些中低基数的字段上选取大量的行

l

已经有位图索引创建于某些或全部的这些中低基数的字段上

l

被查询的表包含很多行

l

可以在单一个表上建立多个位图索引,因此,位图索引能够改善包含冗长WHERE子句的复杂查询的性能,在合计查询和星形模型的联接查询语句中,位图索引也可以提供比较优良的性能

位图索引与B-TREE索引的比较

l

位图索引更节省存储空间

l

位图索引比较适用于数据仓库环境,但不适于联机事务处理环境.在数据仓库环境,数据维护通常上通过批量INSERT和批量UPDATE来完成的,所以索引的维护被延迟直互DML操作结束.举例:当你批量插入1000行数据时,这些插入的行被放置到排序缓存中(SORT BUFFER),然后批处理更新这1000个索引条目,所以,每一个位图段在每一个DML操作中只需更新一次,即使在那个位图段里有多行被更新

l

一个健值的压缩位图是由一个或多个位图段所组成,每一个位图段大约相当于半个BLOCK SIZE那么大,锁的最小粒度是一个位图段,在联机事务处理环境,如果多个事务执行同时的更新(即并发的更新),使用位图索引就会影响UPDATE,INSERT,DELETE性能了

l

一个B-TREE索引的条目只包含一个ROWID,因此,当一个索引条目被锁定,即一行被锁定.但是对于位图索引, 一个索引条目潜在地有可能包含一段ROWID(即某一个范围内的ROWID,有多个ROWID),当一个位图索引条目被锁定时,则这个条目包含的那一段ROWID都被锁定,从而影响并发性.当一个位图段内的ROWID的数量越多时,并发性就越差.虽然如此,对于BULK INSERT,UPDATE和DELETE,位图索引的性能还是比B-TREE索引要好

三,索引和NULL

NULL值在索引中是被看做一个独特值的除非当一个索引的两行或多行的NON-NULL值是相等的情况下.在那种情况下,行被看做是相等的,因此,唯一索引不允许行包含空值以怕被看做是相等的.但是,当所有的行都是空值时,这个规则就不适用.ORACLE并不索引所有健值都为NULL的表的行,除非是位图索引或当簇键字段值是NULL时

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

WHERE子句对某一列使用函数时,除非利用这个简单的技术强制索引,否则Oracle优化器不能查询中使用索引

通常情况下,如果在WHERE子句中不使用诸如UPPER、REPLACE 或SUBSTRD等函数,就不能对指定列建立特定的条件。但如果使用了这些函数,则会出现一个问题:这些函数会阻碍Oracle优化器对列使用索引,因而与采用索引的情况相比较,查询会花费更多的时间。

庆幸的是,如果使用函数的这些列中包含了字符型数据,可以用这样一种方法修改查询语句,以达到强制性使用索引,更有效地运行查询。这篇文章介绍了涉及的技术,并说明了两种典型情况下怎样实现。

大小写混合情况
讨论由于函数修改了列的内容,如何强制使用索引前,让我们首先看看为什么Oracle优化器这种情况下不能使用索引。假定我们要搜寻包含了大小写混合的数据,如ADDRESS表的NAME列。因为数据是用户输入的,我们无法使用已经统一改为大写的数据。为了找到每一个名为john的地址,我们使用包含了UPPER子句的查询语句。如下所示:

SQL> select address from address
where upper(name) like 'JOHN';
运行这个查询语句前,如果我们运行了命令"set autotrace on", 将会得到下列结果,其中包含了执行过程:

ADDRESS

cleveland

1 row selected.

Execution Plan

SELECT STATEMENT

TABLE ACCESS FULL ADDRESS
可以看到,这种情况下,Oracle优化器对ADDRESS 表作了一次完整的扫描,而没有使用NAME 列的索引。这是因为索引是根据列中数据的实际值建立的,而UPPER
函数已经将字符转换成大写,即修改了这些值,因此该查询不能使用这列的索引。优化器不能与索引项比较"JOHN",没有索引项对应于"JOHN"-只有"john"

值得庆幸的是,如果这种情况下想要强制使用索引,有一种简便的方法:只要在WHERE 子句中增加一个或多个特定的条件,用于测试索引值,并减少需要扫描的行,但这并没有修改原来SQL
编码中的条件。以下列查询语句为例:

SQL> select address from address where upper(name) like 'JO%' AND (name

like 'J%' or name like 'j%');
使用这种查询语句(已设置AUTOTRACE),可得到下列结果:

ADDRESS

cleveland

1 row selected.

Execution Plan

SELECT STATEMENT

CONCATENATION

TABLE ACCESS BY INDEX ROWID ADDRESS

INDEX RANGE SCAN ADDRESS_I

TABLE ACCESS BY INDEX ROWID ADDRESS

INDEX RANGE SCAN ADDRESS_I
,优化器为WHERE 子句中AND 联结的两个语句中每一个语句确定的范围进行扫描----第二个语句没有引用函数,因而使用了索引两个范围扫描后,将运行结果合并。

这个例子中,如果数据库有成百千行,可以用下列方法扩充WHERE 子句,进一步缩小扫描范围:

select address from address where

upper(name) like 'JOHN' AND (name like 'JO%'

or name like 'jo%' or name like 'Jo' or name like 'jO' );
得到的结果与以前相同,但是,其执行过程如下所示,表明有4个扫描范围。

Execution Plan

SELECT STATEMENT

CONCATENATION

TABLE ACCESS BY INDEX ROWID ADDRESS

INDEX RANGE SCAN ADDRESS_I

TABLE ACCESS BY INDEX ROWID ADDRESS

INDEX RANGE SCAN ADDRESS_I

TABLE ACCESS BY INDEX ROWID ADDRESS

INDEX RANGE SCAN ADDRESS_I

TABLE ACCESS BY INDEX ROWID ADDRESS

INDEX RANGE SCAN ADDRESS_I
如果试图进一步提高查询速度,我们可以特定的"name like"条件中指明3个或更多的字符。然而,这样做会使得WHERE子句十分笨重。因为需要大小写字符所有可能的组合-joh,Joh,jOh,joH等等。除此之外,指定一个或两个字符已足以加快查询的运行速度了。

让我们看看,当我们引用不同的函数时,怎样运用这个基本技术。
使用REPLACE的情况
正如名字不总是以大写输入一样,电话号码也会以许多格式出现: 如123-456-7890, 123 456 7890,(123)456-7890 等等。

如果列名为 PHONE_NUMBER中搜寻述号码时,可能需要使用函数REPLACE以保证统一的格式。如果PHONE_NUMBER列中只包含空格、连字符和数字,where 子句可以如下所示:

WHERE replace(replace(phone_number , '-' ) , ' ' ) = '1234567890'
WHERE子句两次使用REPLACE 函数去掉了连字符和空格,保证了电话号码是简单的数字串。然而,该函数阻止了优化器该列使用索引。因此,我们按如下方法修改WHERE子句,以强制执行索引

WHERE replace(replace(phone_number, '-' ) , ' ' ) = '1234567890'
AND phone_number like '123% '如果我们知道数据中可能包含圆括号,WHERE 子句会稍微复杂一点。我们可以再增加REPLACE 函数(去掉圆括号、连字符和空格),按如下所示扩充增加的条件:

WHERE replace(replace(replace(replace

(phone_number , ' - ' ) ,' '),'( ' )

, ' ) ' ) = '1234567890' AND

(phone number like ' 123% '

or phone_number like ' (123% ' ) '
该例强调了巧妙地选用WHERE 子句条件的重要性,而且,这些条件不会改变查询结果。你的选择应基于完全了解该列中存的信息类型。该例中,我们需要知道PHONE_NUMBER 数据中存几种
不同的格式,这样,我们能够修改WHERE 子句而不会影响查询结果。
正确的条件
以后当你遇到包含CHARACTER 数据修改函数列的WHERE 子句时,应考虑怎样利用增加一个或两个特定的条件,迫使优化器使用索引。适当地选择一组特定的条件能减少扫描行,并且强制使用索引不会影响查询结果----但却提高了查询的执行速度。

-------------------------------------------------------------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: