用索引提高效率
目前成都创新互联已为1000多家的企业提供了网站建设、域名、雅安服务器托管、网站托管维护、企业网站设计、河南网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
索引是表的一个概念部分 用来提高检索数据的效率 实际上 ORACLE使用了一个复杂的自平衡B tree结构 通常 通过索引查询数据比全表扫描要快 当ORACLE找出执行查询和Update语句的最佳路径时 ORACLE优化器将使用索引 同样在联结多个表时使用索引也可以提高效率 另一个使用索引的好处是 它提供了主键(primary key)的唯一性验证
除了那些LONG或LONG RAW数据类型 你可以索引几乎所有的列 通常 在大型表中使用索引特别有效 当然 你也会发现 在扫描小表时 使用索引同样能提高效率
虽然使用索引能得到查询效率的提高 但是我们也必须注意到它的代价 索引需要空间来
存储 也需要定期维护 每当有记录在表中增减或索引列被修改时 索引本身也会被修改 这意味着每条记录的INSERT DELETE UPDATE将为此多付出 次的磁盘I/O 因为索引需要额外的存储空间和处理 那些不必要的索引反而会使查询反应时间变慢
定期的重构索引是有必要的
ALTER INDEX INDEXNAME REBUILD TABLESPACENAME
索引的操作
ORACLE对索引有两种访问模式
索引唯一扫描 ( INDEX UNIQUE SCAN)
大多数情况下 优化器通过WHERE子句访问INDEX
例如:
表LODGING有两个索引 : 建立在LODGING列上的唯一性索引LODGING_PK和建立在MANAGER列上的非唯一性索引LODGING$MANAGER
SELECT * FROM LODGING
WHERE LODGING = ROSE HILL ;
在内部 上述SQL将被分成两步执行 首先 LODGING_PK 索引将通过索引唯一扫描的方式被访问 获得相对应的ROWID 通过ROWID访问表的方式 执行下一步检索
如果被检索返回的列包括在INDEX列中 ORACLE将不执行第二步的处理(通过ROWID访问表) 因为检索数据保存在索引中 单单访问索引就可以完全满足查询结果
下面SQL只需要INDEX UNIQUE SCAN 操作
SELECT LODGING FROM LODGING WHERE LODGING = ROSE HILL ;
索引范围查询(INDEX RANGE SCAN)
适用于两种情况:
基于一个范围的检索
基于非唯一性索引的检索
例 :
SELECT LODGING FROM LODGING WHERE LODGING LIKE M% ;
WHERE子句条件包括一系列值 ORACLE将通过索引范围查询的方式查询LODGING_PK 由于索引范围查询将返回一组值 它的效率就要比索引唯一扫描低一些
例 :
SELECT LODGING FROM LODGING WHERE MANAGER = BILL GATES ;
这个SQL的执行分两步 LODGING$MANAGER的索引范围查询(得到所有符合条件记录的ROWID) 和下一步同过ROWID访问表得到LODGING列的值 由于LODGING$MANAGER是一个非唯一性的索引 数据库不能对它执行索引唯一扫描
由于SQL返回LODGING列 而它并不存在于LODGING$MANAGER索引中 所以在索引范围查询后会执行一个通过ROWID访问表的操作
WHERE子句中 如果索引列所对应的值的第一个字符由通配符(WILDCARD)开始 索引将不被采用
SELECT LODGING FROM LODGING WHERE MANAGER LIKE %HANMAN ;
在这种情况下 ORACLE将使用全表扫描
基础表的选择
基础表(Driving Table)是指被最先访问的表(通常以全表扫描的方式被访问) 根据优化器的不同 SQL语句中基础表的选择是不一样的
如果你使用的是CBO (COST BASED OPTIMIZER) 优化器会检查SQL语句中的每个表的物理大小 索引的状态 然后选用花费最低的执行路径
如果你用RBO (RULE BASED OPTIMIZER) 并且所有的连接条件都有索引对应 在这种情况下 基础表就是FROM 子句中列在最后的那个表
举例:
SELECT A NAME B MANAGER FROM WORKER A LODGING B
WHERE A LODGING = B LODING;
由于LODGING表的LODING列上有一个索引 而且WORKER表中没有相比较的索引 WORKER表将被作为查询中的基础表
多个平等的索引
当SQL语句的执行路径可以使用分布在多个表上的多个索引时 ORACLE会同时使用多个索引并在运行时对它们的记录进行合并 检索出仅对全部索引有效的记录
在ORACLE选择执行路径时 唯一性索引的等级高于非唯一性索引 然而这个规则只有
当WHERE子句中索引列和常量比较才有效 如果索引列和其他表的索引类相比较 这种子句在优化器中的等级是非常低的
如果不同表中两个想同等级的索引将被引用 FROM子句中表的顺序将决定哪个会被率先使用 FROM子句中最后的表的索引将有最高的优先级
如果相同表中两个想同等级的索引将被引用 WHERE子句中最先被引用的索引将有最高的优先级
举例:
DEPTNO上有一个非唯一性索引 EMP_CAT也有一个非唯一性索引
SELECT ENAME FROM EMP WHERE DEPT_NO = AND EMP_CAT = A ;
这里 DEPTNO索引将被最先检索 然后同EMP_CAT索引检索出的记录进行合并 执行路径如下:
TABLE ACCESS BY ROWID ON EMP AND EQUAL INDEX RANGE SCAN ON DEPT_IDX
INDEX RANGE SCAN ON CAT_IDX
等式比较和范围比较
当WHERE子句中有索引列 ORACLE不能合并它们 ORACLE将用范围比较
举例:
DEPTNO上有一个非唯一性索引 EMP_CAT也有一个非唯一性索引
SELECT ENAME FROM EMP WHERE DEPTNO AND EMP_CAT = A ;
这里只有EMP_CAT索引被用到 然后所有的记录将逐条与DEPTNO条件进行比较 执行路径如下:
TABLE ACCESS BY ROWID ON EMP
INDEX RANGE SCAN ON CAT_IDX
不明确的索引等级
当ORACLE无法判断索引的等级高低差别 优化器将只使用一个索引 它就是在WHERE子句中被列在最前面的
举例:
DEPTNO上有一个非唯一性索引 EMP_CAT也有一个非唯一性索引
SELECT ENAME FROM EMP WHERE DEPTNO AND EMP_CAT A ;
这里 ORACLE只用到了DEPT_NO索引 执行路径如下:
TABLE ACCESS BY ROWID ON EMP
INDEX RANGE SCAN ON DEPT_IDX
我们来试一下以下这种情况:
SQL select index_name uniqueness from user_indexes where table_name = EMP ;
INDEX_NAME UNIQUENES
EMPNO UNIQUE
EMPTYPE NONUNIQUE
SQL select * from emp where empno = and emp_type = A ;
no rows selected
Execution Plan
SELECT STATEMENT Optimizer=CHOOSE
TABLE ACCESS (BY INDEX ROWID) OF EMP
INDEX (RANGE SCAN) OF EMPTYPE (NON UNIQUE)
虽然EMPNO是唯一性索引 但是由于它所做的是范围比较 等级要比非唯一性索引的等式比较低!
强制索引失效
如果两个或以上索引具有相同的等级 你可以强制命令ORACLE优化器使用其中的一个(通过它 检索出的记录数量少)
举例:
SELECT ENAME FROM EMP WHERE EMPNO =
AND DEPTNO + = /*DEPTNO上的索引将失效*/
AND EMP_TYPE || = A /*EMP_TYPE上的索引将失效*/
这是一种相当直接的提高查询效率的办法 但是你必须谨慎考虑这种策略 一般来说 只有在你希望单独优化几个SQL时才能采用它
这里有一个例子关于何时采用这种策略
假设在EMP表的EMP_TYPE列上有一个非唯一性的索引而EMP_CLASS上没有索引
SELECT ENAME FROM EMP WHERE EMP_TYPE = A AND EMP_CLASS = X ;
优化器会注意到EMP_TYPE上的索引并使用它 这是目前唯一的选择 如果 一段时间以后 另一个非唯一性建立在EMP_CLASS上 优化器必须对两个索引进行选择 在通常情况下 优化器将使用两个索引并在他们的结果集合上执行排序及合并 然而 如果其中一个索引(EMP_TYPE)接近于唯一性而另一个索引(EMP_CLASS)上有几千个重复的值 排序及合并就会成为一种不必要的负担 在这种情况下 你希望使优化器屏蔽掉EMP_CLASS索引
用下面的方案就可以解决问题
SELECT ENAME FROM EMP WHERE EMP_TYPE = A AND EMP_CLASS|| = X ;
避免在索引列上使用计算.
WHERE子句中 如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.
举例:
低效
SELECT … FROM DEPT WHERE SAL * ;
高效:
SELECT … FROM DEPT WHERE SAL / ;
自动选择索引
如果表中有两个以上(包括两个)索引 其中有一个唯一性索引 而其他是非唯一性.
在这种情况下 ORACLE将使用唯一性索引而完全忽略非唯一性索引.
举例:
SELECT ENAME FROM EMP WHERE EMPNO =
AND DEPTNO = ;
这里 只有EMPNO上的索引是唯一性的 所以EMPNO索引将用来检索记录.
TABLE ACCESS BY ROWID ON EMP
INDEX UNIQUE SCAN ON EMP_NO_IDX
避免在索引列上使用NOT
通常 我们要避免在索引列上使用NOT NOT会产生在和在索引列上使用函数相同的
影响 当ORACLE 遇到 NOT 他就会停止使用索引转而执行全表扫描
举例:
低效: (这里 不使用索引)
SELECT … FROM DEPT WHERE DEPT_CODE NOT = ;
高效: (这里 使用了索引)
SELECT … FROM DEPT WHERE DEPT_CODE ;
需要注意的是 在某些时候 ORACLE优化器会自动将NOT转化成相对应的关系操作符
NOT to =
NOT = to
NOT to =
NOT = to
SQL select * from emp where NOT empno ;
no rows selected
Execution Plan
SELECT STATEMENT Optimizer=CHOOSE
TABLE ACCESS (BY INDEX ROWID) OF EMP
INDEX (RANGE SCAN) OF EMPNO (UNIQUE)
SQL select * from emp where empno = ;
no rows selected
Execution Plan
SELECT STATEMENT Optimizer=CHOOSE
TABLE ACCESS (BY INDEX ROWID) OF EMP
INDEX (RANGE SCAN) OF EMPNO (UNIQUE)
两者的效率完全一样 也许这符合作者关于 在某些时候 ORACLE优化器会自动将NOT转化成相对应的关系操作符 的观点.
用=替代
如果DEPTNO上有一个索引
高效:
SELECT * FROM EMP WHERE DEPTNO =
低效:
SELECT * FROM EMP WHERE DEPTNO
lishixinzhi/Article/program/Oracle/201311/17710
位图索引是Oralce数据库索引中的异类,其在某些比较特殊的场合中有突出的表现。一般来说,位图索引的效果直接跟列的基数相关。为此在谈到如何提高位图索引的使用效果时,也往往跟这个列的基数相关。为此必须对这个列的基数有一个清晰的认识。
一、什么时候改采用位图索引
基数是指某个列可能拥有的不重复数值的个数。比如说在一个中华人民共和国公民的信息表中,包含着十几亿条的记录。但是在这些记录中,有几个比较特殊的列,其指包含几个有限的重复数值。如性别这一字段,其就只有男与女两个可能值;如在民族一列内也只有56个不重复的值;如在出身地一列内,中国只有34个省市自治区。
有时候用户可能需要根据这些列来查询相关的数据。如公司搞活动的时候,可能需要确认一下活动有没有涉及到宗教的禁忌,如可能涉及到回族的禁忌的话,那么就需要查询一下员工信息系统,看看公司有没有回族的员工。如公司可能在三八妇女节的时候,会给女员工提供一定的福利,为此也需要在数据库中拉出女员工的名单,此时也就是根据性别这个字段来查询数据。等等。如果用户查询的列的基数非常的小,只是有限的几个固定值,如性别、民族、行政区等等。要为这些基数值比较小的列建立索引时,就需要建立位图索引。如果一定要说一个具体的标准的话,那么笔者认为如果基数值在整个表记录的2%以内或者字段内容的重复值在100次以上,则通过位图索引可以起到不错的效果。
大部分情况下都是通过基数值来确定是否需要使用位图索引。但是还有一种比较特殊的情况,可能这个列的基数值非常的大,也就是说这个列中的值重复性不是很高。但是只要起满足一定的条件,那么在这个字段上创建位图索引,也可以起到不错的效果。一般来说,如果字段往往在Where查询条件语句中被用到,并且采用的运算符为AND
或者OR 的逻辑运算符号的话,那么其效果也比建议其他索引要好的多。
二、把需要建立位图索引的列设置为固定长度的数据类型。
在Oracle数据库中,数据类型从大的来说,包括固定长度的数据类型与可变长度的数据类型。如就拿字符型数据来说,就有固定长度的字符串类型(char)与可变长度的字符串类型(VARCHAR2
)。这两个数据类型都是字符串数据类型,它们的差别主要在于字符串长度的控制上。CHAR存储固定长度的字符串。如果实际存储的字符串没有达到其规定的最大值的话,则数据库系统会自动在后面加上空格。而VARCHAR2则主要用来存储可变长度的字符数据类型。如存储固定长度的CHAR字符串类型不同,如果在这个列中存储的数据没有达到规定的最大值,
则数据库系统不会在最后加入空格,以实际的字符串存储。如果从数据库的体积上来看,肯定是可变长的数据类型比较好。
但是如果从位图索引的效果上来看,则最好把建立位图索引的列设置为固定长度的数据类型。因为位图索引使用固定长度的数据类型要比可变长度的数据类型在性能上要更加的优越。也就是说,如果要在某个字符类型的列上建立位图索引,那么最好把这个列的数据类型设置为char(即使其实际存储的长度不同),而不是设置额外NVCHAR2。因为相对于性能的提升来说,这点空间的损失仍然是值得的。
另外我们也可以通过其他一些手段来保证列中存储内存长度的一致。如拿中国的行政区为例,大部分行政区就是3个字,如浙江省;但是长度比较长的有8个字符,如新疆维吾尔自治区。如果记录比较多的话,累积下来确实会造成比较大的浪费。在这种情况下,数据库管理在设计的时候,可以采用一些折中的处理方式。如利用简称来代表各个省份的名字。还有一种就是在存储的时候利用数字来表示省份,然后再在另外一张表中建立数字与省份之间的对应关系。笔者建议采用的是后面一种方式。
总之如果要为某个列建立位图索引的话,则数据库管理员最好能够想方设法让这个字段采用固定长度的数据类型,有时候即使牺牲一点存储空间也是在所不辞。
三、位图索引的使用限制。
虽然说位图索引在基数比较小的列或者使用到逻辑运算符时能够显著的提高数据库的查询性能,但是其在使用的时候仍然有不少的限制。数据库管理员需要了解这些限制,如此的话在数据库设计时才不会竹篮子打水一场空。
如把某个字段设置为位图索引的话,最好采用固定长度的数据类型。同时这个长度有最大的限制。在最新的Oracle数据库版本中,这个位图索引的最大长度不能够超过30。不知道以后的版本中会不会放宽这个长度上的限制。如位图索引不能够被声明为唯一索引;如位图索引不包含认为列数据,并且不能够用于任何类型的完整性检查;如当执行ALTER
TABLE语句并修改包含有位图索引的列时,这会使位图索引失效。
最重大的一个限制是基于规则的优化器不会考虑位图索引。Oracle数据库系统在执行一个SQL之前,首先要分析一下语句的执行计划,然后再按执行计划去执行。分析语句的执行计划的工作是由优化器来完成的。Oracle的优化器共有两种的优化方式,即基于规则的优化方式和基于成本的优化方式。基于规则的优化方式是指优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则。基于成本的优化方式是看语句的执行成本。这里的成本主要指Cpu和内存。优化器在判断是否用这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有少行、每行的长度等信息。然后数据库系统会根据这些信息来确定是否需要采用这个优化方式。另外还有根据这两个优化模式衍生出来的优化器。
总之数据库管理理员需要明确的一点就是,如果要采用位图索引的话,则最好把数据库的优化器选择为基于成本的优化器模式。因为如果数据库采用的是基于规则的优化器模式的话,则会忽略位图索引。那么此时数据库管理员所建立的位图索引将一无用处。这一点大部分数据库管理员可能并不清楚,一定要引以为鉴。
四、同一个查询中合并多个位图索引。
通常情况下在同一个查询中合并多个位图索引后,可以使得数据库的查询性能得到显著的提高。也就是说,如在员工信息表中有性别、婚姻状态、职称等字段。这些字段都是基数比较低的字段,可以用来创建位图索引。如果现在用户在查询的时候,需要根据上面这三个字段来查询员工的信息,如需要查找性别为女、婚姻状态为已婚、职称为经理层以上的员工信息,为他们举行一个家庭派对的活动。此时在查询条件中就需要用到这个三个位图索引字段。数据库在执行查询计划的时候,如果Where字句中包含了这些位图索引字段的话,择优化器会依次使用3个单独的位图索引。每个位图记录指针,用于指示表中哪些行包含位图中的已知值。有了这些重要信息之后,数据库会执行一个位图AND操作并将从所有的四个位图中返回哪些行。然后再把这些值转换为ROWID值,并且查询继续完成剩余的处理工作(根据ROWID的值查询其他的信息)。
也就是说,如果在一个查询条件语句中,采用了多个位图索引来进行查询的话,其查询的效果是1+1〉2的效果。为此在应用程序设计中,可以把一些位图索引的字段作为查询条件都放置在查询窗口中,以明示的方式让用户选择查询条件。这对于提高应用程序的查询性能具有很大的帮助。
在Oracle数据库中一个表中最大可以支持30个位图索引。当然实际工作中往往用不了这么多。因为通常情况下只有在基数值比较小的列上利用这个位图索引才能够起到比较好的效果。相反如果在一些基数值比较大的列上采用位图索引的话,反而会起到相反的效果。而在一张表中基数列小的值不会很大。所以说数据库管理员更加需要关注这些基数列比较小的值。如果用户有对这些基数值小的列进行查询的习惯,那么数据库管理员就不能够有任何吝啬,要马上把这些列设置为位图索引。并且在查询设计的时候,最好能够在同一个查询中合并多个位图索引,以提高查询的性能。
需要用索引来解决,索引的创建规则如下:
1、表的
主键
、
外键
必须有索引;
2、数据量超过300的表应该有索引;
3、经常与其他表进行连接的表,在连接字段上应该建立索引;
4、经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
5、索引应该建在选择性高的字段上;
6、索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
7、复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
A、
正确选择
复合索引中的主列字段,一般是选择性较好的字段;
B、复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;
C、如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
D、如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;
E、如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;
8、频繁进行
数据操作
的表,不要建立太多的索引;
9、删除无用的索引,避免对执行计划造成负面影响;
以上是一些普遍的建立索引时的判断依据。一言以蔽之,索引的建立必须慎重,对每个索引的必要性都应该经过仔细分析,要有建立的依据。因为太多的索引与不充分、不正确的索引对性能都毫无益处:在表上建立的每个索引都会增加存储开销,索引对于插入、删除、更新操作也会增加处理上的开销。另外,过多的复合索引,在有单字段索引的情况下,一般都是没有
存在价值
的;相反,还会降低数据增加删除时的性能,特别是对频繁更新的表来说,负面影响更大。