oracle sql优化

第一掌 避免对列的操作

任何对列的操作都可能导致全表扫描,这里所谓的操作包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等式的右边,甚至去掉函数。

例1:下列SQL条件语句中的列都建有恰当的索引,但30万行数据情况下执行速度却非常慢:

select * from record where  substrb(CardNo,1,4)=‘5378‘(13秒)

select * from record where  amount/30< 1000(11秒)

select * from record where  to_char(ActionTime,‘yyyymmdd‘)=‘19991201‘(10秒)

由于where子句中对列的任何操作结果都是在SQL运行时逐行计算得到的,因此它不得不进行表扫描,而没有使用该列上面的索引;如果这些结果在查询编译时就能得到,那么就可以被SQL优化器优化,使用索引,避免表扫描,因此将SQL重写如下:

select * from record where CardNo like  ‘5378%‘(< 1秒)

select * from record where amount  < 1000*30(< 1秒)

select * from record where ActionTime= to_date (‘19991201‘ ,‘yyyymmdd‘)(< 1秒)

差别是很明显的!

第二掌 避免不必要的类型转换

需要注意的是,尽量避免潜在的数据类型转换。如将字符型数据与数值型数据比较,ORACLE会自动将字符型用to_number()函数进行转换,从而导致全表扫描。

例2:表tab1中的列col1是字符型(char),则以下语句存在类型转换:

select col1,col2 from tab1 where col1>10,

应该写为: select col1,col2 from tab1 where col1>‘10‘。

第三掌 增加查询的范围限制

增加查询的范围限制,避免全范围的搜索。

例3:以下查询表record 中时间ActionTime小于2001年3月1日的数据:

select * from record where ActionTime < to_date (‘20010301‘ ,‘yyyymm‘)

查询计划表明,上面的查询对表进行全表扫描,如果我们知道表中的最早的数据为2001年1月1日,那么,可以增加一个最小时间,使查询在一个完整的范围之内。修改如下: select * from record where

ActionTime < to_date (‘20010301‘ ,‘yyyymm‘)

and   ActionTime > to_date (‘20010101‘ ,‘yyyymm‘)

后一种SQL语句将利用上ActionTime字段上的索引,从而提高查询效率。把‘20010301‘换成一个变量,根据取值的机率,可以有一半 以上的机会提高效率。同理,对于大于某个值的查询,如果知道当前可能的最大值,也可以在Where子句中加上 “AND 列名< MAX(最大值)”。

第四掌 尽量去掉"IN"、"OR"

含有"IN"、"OR"的Where子句常会使用工作表,使索引失效;如果不产生大量重复值,可以考虑把子句拆开;拆开的子句中应该包含索引。

例4:     select count(*) from stuff where id_no in(‘0‘,‘1‘)(23秒)

可以考虑将or子句分开:

select count(*) from stuff where id_no=‘0‘

select count(*) from stuff where id_no=‘1‘

然后再做一个简单的加法,与原来的SQL语句相比,查询速度更快。

第五掌 尽量去掉 "<>"

尽量去掉 "<>",避免全表扫描,如果数据是枚举值,且取值范围固定,则修改为"OR"方式。

例5:

UPDATE SERVICEINFO SET STATE=0 WHERE STATE<>0;

以上语句由于其中包含了"<>",执行计划中用了全表扫描(TABLE ACCESS FULL),没有用到state字段上的索引。实际应用中,由于业务逻辑的限制,字段state为枚举值,只能等于0,1或2,而且,值等于=1,2的很 少,因此可以去掉"<>",利用索引来提高效率。

修改为:UPDATE SERVICEINFO SET STATE=0  WHERE STATE = 1 OR STATE = 2 。进一步的修改可以参考第4种方法。

第六掌 去掉Where子句中的IS NULL和IS NOT NULL

Where字句中的IS NULL和IS NOT NULL将不会使用索引而是进行全表搜索,因此需要通过改变查询方式,分情况讨论等方法,去掉Where子句中的IS NULL和IS NOT NULL。

第七掌 索引提高数据分布不均匀时查询效率

索引的选择性低,但数据的值分布差异很大时,仍然可以利用索引提高效率。A、数据分布不均匀的特殊情况下,选择性不高的索引也要创建。

表ServiceInfo中数据量很大,假设有一百万行,其中有一个字段DisposalCourseFlag,取值范围为枚举值: [0,1,2,3,4,5,6,7]。按照前面说的索引建立的规则,“选择性不高的字段不应该建立索引,该字段只有8种取值,索引值的重复率很高,索引选 择性明显很低,因此不建索引。然而,由于该字段上数据值的分布情况非常特殊,具体如下表:


取值范围


1~5


6


7


占总数据量的百分比


1%


98%


1%

而且,常用的查询中,查询DisposalCourseFlag<6 的情况既多又频繁,毫无疑问,如果能够建立索引,并且被应用,那么将大大提高这种情况的查询效率。因此,我们需要在该字段上建立索引。

第八掌 利用HINT强制指定索引

在ORACLE优化器无法用上合理索引的情况下,利用HINT强制指定索引。

继续上面7的例子,ORACLE缺省认定,表中列的值是在所有数据行中均匀分布的,也就是说,在一百万数据量下,每种 DisposalCourseFlag值各有12.5万数据行与之对应。假设SQL搜索条件DisposalCourseFlag=2,利用 DisposalCourseFlag列上的索引进行数据搜索效率,往往不比全表扫描的高,ORACLE因此对索引“视而不见”,从而在查询路径的选择 中,用其他字段上的索引甚至全表扫描。根据我们上面的分析,数据值的分布很特殊,严重的不均匀。为了利用索引提高效率,此时,一方面可以单独对该字段或该 表用analyze语句进行分析,对该列搜集足够的统计数据,使ORACLE在查询选择性较高的值时能用上索引;另一方面,可以利用HINT提示,在 SELECT关键字后面,加上“/*+ INDEX(表名称,索引名称)*/”的方式,强制ORACLE优化器用上该索引。

比如: select * from  serviceinfo where DisposalCourseFlag=1 ;

上面的语句,实际执行中ORACLE用了全表扫描,加上蓝色提示部分后,用到索引查询。如下:

select /*+  INDEX(SERVICEINFO,IX_S_DISPOSALCOURSEFLAG)  */  *

from  serviceinfo where DisposalCourseFlag=1;

请注意,这种方法会加大代码维护的难度,而且该字段上索引的名称被改变之后,必须要同步所有指定索引的HINT代码,否则HINT提示将被ORACLE忽略掉。

第九掌 屏蔽无用索引

继续上面8的例子,由于实际查询中,还有涉及到DisposalCourseFlag=6的查询,而此时如果用上该字段上的索引,将是非常不明智 的,效率也极低。因此这种情况下,我们需要用特殊的方法屏蔽该索引,以便ORACLE选择其他字段上的索引。比如,如果字段为数值型的就在表达式的字段名 后,添加“+ 0”,为字符型的就并上空串:“||""”

如: select * from  serviceinfo where DisposalCourseFlag+ 0 = 6 and workNo =  ‘36‘ 。

不过,不要把该用的索引屏蔽掉了,否则同样会产生低效率的全表扫描。

第十掌 分解复杂查询,用常量代替变量

对于复杂的Where条件组合,Where中含有多个带索引的字段,考虑用IF语句分情况进行讨论;同时,去掉不必要的外来参数条件,减低复杂度,以便在不同情况下用不同字段上的索引。

继续上面9的例子,对于包含

Where (DisposalCourseFlag < v_DisPosalCourseFlag) or (v_DisPosalCourseFlag is null) and ....的查询,(这里v_DisPosalCourseFlag为一个输入变量,取值范围可能为[NULL,0,1,2,3,4,5,6,7]),可以 考虑分情况用IF语句进行讨论,类似:

IF v_DisPosalCourseFlag =1 THEN

Where DisposalCourseFlag = 1 and ....

ELSIF v_DisPosalCourseFlag =2 THEN

Where DisposalCourseFlag = 2 and ....

。。。。。。

第十一掌 like子句尽量前端匹配

因为like参数使用的非常频繁,因此如果能够对like子句使用索引,将很高的提高查询的效率。

例6:select * from city where name like ‘%S%’

以上查询的执行计划用了全表扫描(TABLE ACCESS FULL),如果能够修改为:

select * from city where name like ‘S%’

那么查询的执行计划将会变成(INDEX RANGE SCAN),成功的利用了name字段的索引。这意味着Oracle SQL优化器会识别出用于索引的like子句,只要该查询的匹配端是具体值。因此我们在做like查询时,应该尽量使查询的匹配端是具体值,即使用 like ‘S%’。

第十二掌 用Case语句合并多重扫描

我们常常必须基于多组数据表计算不同的聚集。例如下例通过三个独立查询:

例8:1)select count(*) from emp where sal<1000;

2)select count(*) from emp where sal between 1000 and 5000;

3)select count(*) from emp where sal>5000;

这样我们需要进行三次全表查询,但是如果我们使用case语句:

select

count (sale when sal <1000

then 1 else null end)             count_poor,

count (sale when between 1000 and 5000

then 1 else null end)             count_blue_collar,

count (sale when sal >5000

then 1 else null end)             count_poor

from emp;

这样查询的结果一样,但是执行计划只进行了一次全表查询。

第十三掌 使用nls_date_format

例9:

select * from record where  to_char(ActionTime,‘mm‘)=‘12‘

这个查询的执行计划将是全表查询,如果我们改变nls_date_format,

SQL>alert session set nls_date_formate=’MM’;

现在重新修改上面的查询:

select * from record where  ActionTime=‘12‘

这样就能使用actiontime上的索引了,它的执行计划将是(INDEX RANGE SCAN)。

第十四掌 使用基于函数的索引

前面谈到任何对列的操作都可能导致全表扫描,例如:

select * from emp where substr(ename,1,2)=’SM’;

但是这种查询在客服系统又经常使用,我们可以创建一个带有substr函数的基于函数的索引,

create index emp_ename_substr on eemp ( substr(ename,1,2) );

这样在执行上面的查询语句时,这个基于函数的索引将排上用场,执行计划将是(INDEX RANGE SCAN)。

第十五掌 基于函数的索引要求等式匹配

上面的例子中,我们创建了基于函数的索引,但是如果执行下面的查询:

select * from emp where substr(ename,1,1)=’S’

得到的执行计划将还是(TABLE ACCESS FULL),因为只有当数据列能够等式匹配时,基于函数的索引才能生效,这样对于这种索引的计划和维护的要求都很高。请注意,向表中添加索引是非常危险的 操作,因为这将导致许多查询执行计划的变更。然而,如果我们使用基于函数的索引就不会产生这样的问题,因为Oracle只有在查询使用了匹配的内置函数时 才会使用这种类型的索引。

第十六掌 使用分区索引

在用分析命令对分区索引进行分析时,每一个分区的数据值的范围信息会放入Oracle的数据字典中。Oracle可以利用这个信息来提取出那些只与SQL查询相关的数据分区。

例如,假设你已经定义了一个分区索引,并且某个SQL语句需要在一个索引分区中进行一次索引扫描。Oracle会仅仅访问这个索引分区,而且会在这个分区上调用一个此索引范围的快速全扫描。因为不需要访问整个索引,所以提高了查询的速度。

第十七掌 使用位图索引

位图索引可以从本质上提高使用了小于1000个唯一数据值的数据列的查询速度,因为在位图索引中进行的检索是在RAM中完成的,而且也总是比传统的B树索引的速度要快。对于那些少于1000个唯一数据值的数据列建立位图索引,可以使执行效率更快。

第十八掌 决定使用全表扫描还是使用索引

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

1)对于原始排序的表  仅读取少于表记录数40%的查询应该使用索引范围扫描。反之,读取记录数目多于表记录数的40%的查询应该使用全表扫描。

2)对于未排序的表    仅读取少于表记录数7%的查询应该使用索引范围扫描。反之,读取记录数目多于表记录数的7%的查询应该使用全表扫描。

2         总结

以上的招式,是完全可以相互结合同时运用的。而且各种方法之间相互影响,紧密联系。这种联系既存在一致性,也可能带来冲突,当冲突发生时,需要根据实际情况进行选择,没有固定的模式。最后决定SQL优化功力的因素就是对ORACLE内功的掌握程度了。

另外,值得注意的是:随着时间的推移和数据的累计与变化,ORACLE对SQL语句的执行计划也会改变,比如:基于代价的优化方法,随着数据量的增 大,优化器可能错误的不选择索引而采用全表扫描。这种情况可能是因为统计信息已经过时,在数据量变化很大后没有及时分析表;但如果对表进行分析之后,仍然 没有用上合理的索引,那么就有必要对SQL语句用HINT提示,强制用合理的索引。但这种HINT提示也不能滥用,因为这种方法过于复杂,缺乏通用性和应 变能力,同时也增加了维护上的代价;相对来说,基于函数右移、去掉“IN ,OR ,<> ,IS NOT NULL ”、分解复杂的SQL语句等等方法,却是“放之四海皆准”的,可以放心大胆的使用。

同时,优化也不是“一劳永逸”的,必须随着情况的改变进行相应的调整。当数据库设计发生变化,包括更改表结构:字段和索引的增加、删除或改名等;业务逻辑发生变化:如查询方式、取值范围发生改变等等。

时间: 2024-07-30 03:02:07

oracle sql优化的相关文章

oracle sql优化技巧

数据库方面一直是自己的薄弱项,现在以本文慢慢积累总结oracle sql优化的一些技巧. 1.首先大家很容易想到的一切优化技巧--索引,索引有啥用?索引在表数据量很大时添加索引确实能加快查询速度,通过索引查询能很好地避免全表扫描. 但应该也要注意的时这是在数据量较大的时候.同时数据较小时,反而浪费索引空间.另外,添加索引之后数据的插入,更新反而会变慢,在插入或修改记录 时需要新建索引并排序. 索引创建语句: create [unique] index xxx on A(column 1,colu

【重磅干货】看了此文,Oracle SQL优化文章不必再看!

听“俊”一席话,胜读十年书.看了这篇由DBA+社群联合发起人丁俊大师(网名:dingjun123)分享的SQL优化大作,其他Oracle SQL优化文章都不必再看了! 专家简介 丁俊 网名:dingjun123 DBA+社群联合发起人 性能优化专家,Oracle ACEA,ITPUB开发版资深版主.8年电信行业从业经验,在某大型电信系统提供商工作7年,任资深工程师,从事过系统开发与维护.业务架构和数据分析.系统优化等工作.擅长基于ORACLE的系统优化,精通SQL.PL/SQL.JAVA等.电子

Oracle SQL优化一(常见方法)

1.表访问方式优化: a)普通表优先“Index Lookup 索引扫描”,避免全表扫描 大多数场景下,通过“Index Lookup 索引扫描”要比“Full Table Scan (FTS) 全表扫描”效率要高的多.在编写SQL时,为了保证查询能够使用索引,需要避免出现如下场景: is null 和 is not null 在oracle中null是不能够作为索引的,如果某列数据中有“null”,不要在该列上创建索引,即使创建,也不会提高查询性能. 而在SQL语句中,如果使用is null和

oracle sql优化笔记

oracle优化一般分为:1.sql优化(现在oracle都会根据sql语句先进行必要的优化处理,这种应该用户不大了,但是像关联和嵌套查询肯定是和影响性能的) A.oracle的sql语句的条件是从右往左执行的,如下语句:select * from t_user where nation='回族' and age > 20.oracle首先是查询年龄大于20岁的,再查询民族为回族的,最后汇总两次得到的结果. B.根据A中说的sql语句执行特点,上面的语句是可以进行优化的.A中的sql语句查询条件

Oracle SQl优化总结

连续两个公司都作为外派人员到客户方工作,缺少归属感的同时,对数据库技术的热爱是我唯一的安慰,毕竟这是自己喜欢的事情,还可以做下去. 因为客户项目的需要,我又开始接触Oracle,大部分工作在工作流的优化和业务数据的排查上.为了更好的做这份工作,我有参考过oracle达人,Oracle.10g性能分析与优化思路,基于海量数据的数据库设计与优化等书籍,以及案例学习SQL优化的视频等.基本上我工作中接触的主要是Oracle SQl的优化,基于长时间做SQL优化工作,现在对Oracle的SQL优化做一下

Oracle sql优化必知——表的访问

<访问数据的方法> 访问表中的数据有两种:1.直接访问表   2.先访问索引,再回表 1.直接访问表的两种方法: ①.全表扫描 全表扫描是指Oracle在访问目标表的数据时,会从该表所占用的第一个区(extent)的第一个块(block)开始扫描,一直扫描到该表的高水位线,这段范围内的所有数据库都必须读到,当然如果目标sql的where中指定的过滤条件,最后只返回满足条件的数据即可:(有时候全表扫描的效率还是非常高的,但是随着表的数据增多 资源消耗也会在逐步增加) ②.rowid扫描 rowi

好记性不如烂笔头之Oracle SQL优化(2)

*sql优化基于oracle11gR2读书笔记* 三.Oracle里的Cursor Oracle中的Cursor是Oracle数据库中SQL解析和执行的载体,是c语言的一种数据结构(oracle是用c写的). Oracle数据库中的Cursor分为两种:一种是Shared Cursor,另外一种是Session Cursor. 1.Shared Cursor 先来了解一下什么是库缓存.库缓存对象. 我们知道Oracle中有一个全局内存区域SGA,而SGA又可以分为java池.大池.共享池.空池等

[terry笔记]Oracle SQL 优化之sql tuning advisor (STA)

前言:经常可以碰到优化sql的需求,开发人员直接扔过来一个SQL让DBA优化,然后怎么办? 当然,经验丰富的DBA可以从各种方向下手,有时通过建立正确索引即可获得很好的优化效果,但是那些复杂SQL错综复杂的表关联,却让DBA们满头大汗. 如下特别介绍一种oracle官方提供的科学优化方法STA,经过实践,不敢说此特性绝对有效,但是可以开阔思路,并且从中学到许多知识,不再用“猜”的方式去创建索引了. SQL优化器SQL Tuning Advisor (STA),是oracle的sql优化补助工具.

Oracle SQL 优化规则

一.问题的提出 在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用 系统提交实际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一.系统优化中一个很重要的方面就是SQL语句的优 化.对于海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的 SQL语句,提高系统的可用性. 在多数情况下,Oracle