Oracle数据库的sql语句性能优化

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

  Oracle的sql调优第一个复杂的主题,甚至需要长篇概论来介绍OracleSQL调优的细微差别。不过有一些基本的规则是每个OracleDBA都需要遵从的,这些规则可以改善他们系统的性能。

  sql调优的目标是简单的:消除不必要的大表全表搜索。不必要的全表搜索导致大量不必要的磁盘I/O,从而拖慢整个数据库的性能,对于不必要的全表搜索来说,最常见的调优方法是增加索引,可以在表中加入标准的B树索引,也可以加入位图索引和基于函数的索引。要决定是否消除一个全表搜索,你可以仔细检查索引搜索的I/O开销和全表搜索的开销,它们的开销和数据块的读取和可能的并行执行有关,并将两者作对比。

  另外,在全表搜索是一个最快的访问方法时,将小表的全表搜索放到缓存(内存)中,也是一个非常明智的选择。我们会发现现在诞生了很多基于内存的数据库管理系统,将整个数据库置于内存之中,性能将得到质的飞跃。

一、与索引相关的性能优化

  在多数情况下,Oracle使用索引来更快地遍历表,优化器主要根据定义的索引来提高性能。但是,如果在sql语句的where子句中写的sql代码不合理,就会造成优化器删去索引而使用全表扫描,一般这种sql语句就是所谓的劣质sql语句。在编写sql语句时我们应清楚优化器根据何种原则来删除索引,这有助于写出高性能的sql语句。

1.IS NULL 与 IS NOT NULL

  不能用null做索引,任何包含null值的列都将不会被包含在索引中,即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说某列存在空值,即使对该列建立索引也不会提高性能。任何在where子句中使用is null或者is nou null的语句优化器是不允许使用索引的。

2.联接列

  对于有联接的列,即使最后的联接列为一个静态值,优化器是不会使用索引的。来看个例子,假定有一个职工表(employee),对于一个职工的姓和名分成两列存放(FIRST_NAME和LAST_NAME),现在要查询一个叫Beill Cliton的职工。

  下面是一个采用联接查询的sql语句:

select * from employee where first_name ||‘‘|| last_name = ‘Beill Cliton‘;

  上面这条语句完全可以查询出是否有Beill Cliton这个员工,但是这里需要注意,系统优化器对基于LAST_NAME创建的索引没有使用,当采用下面这种sql语句的编写,Oracle系统就可以采用基于LAST_NAME创建的索引:

select * from employee where first_name = ‘Beill‘ and last_name = ‘Cliton‘;

3.带通配符(%)的like语句

  同样拿上面的例子,目前的需求是这样的,要求在职工表中查询名字中包含Cliton的人,可以采用如下的查询sql语句:

select * from employee where last_name like ‘%Cliton%‘;

  这里由于通配符(%)在搜寻词首出现,所以Oracle系统无法使用last_name的索引。在很多情况下可能无法避免这种情况,但是一定要心里有底,通配符这样使用会降低查询速度。然而当通配符出现在字符串其它位置时,优化器就能利用索引,在下面的查询中索引就得到了使用:

select * from employee where last_name like ‘C%‘;

  该语句查询所有姓名以C开头的,这完全满足索引的要求,因为索引本身就是一个排序的列。

4.ORDER BY 子句

  ORDER BY子句决定了Oracle如何将返回的查询结果排序。该子句对要排序的列没有什么特别的限制,也可以将函数加入到列中(象联接或者附加等)。任何在该子句的非索引项或者有计算表达式都将降低查询速度。

  仔细检查order by子语句找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by子句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。

5.NOT 关键字

  我们在查询时经常在where子句使用一些逻辑表达式,如大于、小于、等于以及不等于等等,也可以使用and(与)、or(或)以及not(非)。NOT可用来对任何逻辑运算取反。下面是一个NOT子句的例子:

... where not(status = ‘VALID‘);

  如果要使用NOT,则应在取反的短语前面加上括号,并在短语前面加上NOT运算符。NOT运算符包含在另外一个逻辑运算符中,这就是不等于(<>)运算符。换句话说,即使不在查询where子句中加入NOT关键字,NOT仍在运算符中,见下例:

... where status <> ‘VALID‘;

  再看下面这个例子:

select * from employee where salary <> 3000;

  对这个查询,可以改写为不使用NOT:

select * from employee where salary < 3000 or salary > 3000;

  虽然这两种查询的结果是一样的,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oralce对salary列使用索引,而第一种查询不能使用索引。

6.IN 和 EXISTS

  有时候会将一列和一系列值相比较,最简单的办法就是在where子句中使用子查询。在where子句中可以使用两种格式的子查询。

  第一种格式是使用IN操作符:

... where column in (select * from ... where ...);

  第二种格式是使用EXISTS操作符:

... where exists (select ‘X‘ from ... where ...);

  相信绝大多数人会使用第一种格式,因为它比较容易编写,而实际上第二种格式要远比第一种格式的效率高。在Oracle中可以几乎将所有的IN操作符子查询改写为使用EXISTS的子查询。

  第二种格式中,子查询以“select ‘X‘”开始,运用EXISTS子句,不管子查询从表中抽取什么数据,它只查看where子句,这样优化器就不必遍历整个表而仅根据索引就可完成工作(这里假定在where语句中使用的列存在索引)。相对于IN子句来说,EXISTS使用相连子查询构造起来要比IN子查询困难一些。

  通过使用EXISTS,Oracle系统会首先检查主查询,然后运行子查询直到它找到第一个匹配项,这就节省了时间。Oralce系统在执行IN子查询时,首先执行子查询,并将获得的结果列表放在一个加了索引的临时表中。在执行子查询之前,系统先将主查询挂起,待子查询执行完毕,存放在临时表中以后再执行主查询。这也就是使用EXISTS比使用IN通常查询速度快的原因。

  同时应尽可能使用NOT EXISTS来代替NOT IN,尽管二者都使用了NOT(不能使用索引而降低速度),NOT EXISTS要比NOT IN查询效率更高。

7.<> 不等于符号

  不等于操作符是永远不会用到索引的,因为对它的处理只会产生全表扫描。

  推荐方案:用其它相同功能的操作运算符代替,如

  a<>0 改为 a>0 or a<0,  a<>‘‘ 改为 a >‘‘

8.避免在索引列上使用计算

  WHERE子句中,如果索引列是函数的一部分,优化器将不使用索引而使用全表扫描

  低效:

select ... from dept where SAL * 12 > 25000;

  高效:

select ... from dept where SAL > 25000/12;

9.总是使用索引的第一个列

  如果索引是建立在多个列上,只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引。这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引。

10.避免改变索引列的类型

  当比较不同数据类型的数据时,Oralce自动对列进行简单的类型转换。

  假设empno是一个数值类型的索引列:

select ... from emp where empno = ‘123‘;

  实际上,经过了Oracle类型转换,语句转化为:

select ... from emp where empno = TO_NUMBER(‘123‘);

  幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变。

  现在,假设emp_type是一个字符类型的索引列:

select ... from emp where emp_type = 123;

  这个语句被Oracle转换为:

select ... from emp where TO_NUMBER(emp_type) = 123;

  因为内部发生的类型转换,这个索引将不会被用到。为了避免Oracle对sql进行隐式的类型转换,最好把类型转换用显示表现出来。注意当字符和数值比较时,Oracle会优先转换数值类型到字符类型。

11.需要当心的 WHERE 子句

  某些select语句中的where子句不使用索引:

  • ‘!=‘将不使用索引,索引只能告诉你什么存在于表中,而不能告诉你什么不存在于表中
  • ‘||‘是字符连接函数,就像其它函数那样,停用了索引
  • ‘+‘是数学函数,就像其它数学函数那样,停用了索引
  • 相同的索引列不能互相比较,这将会启动全表扫描

12.其它一些规则

  • 如果检索数据量超过30%的表中记录数,使用索引将没有显著的效率提高
  • 在特定情况下,使用索引也许会比全盘扫描慢,但这是同一个数量级上的区别。而通常情况下,使用索引比全表扫描要快几倍乃至几千倍
  • 避免在索引列上使用IS NULL 和IS NOT NULL
  • 避免在索引列上使用NOT
  • 用EXISTS替代IN、用NOT EXISTS替代NOT IN
  • 通过内部函数提高sql效率
  • 选择最有效的表名顺序:Oracle的解析器按照从右到左的顺序处理from子句中的表名,from子句中写在最后的表(基础表)将被最先处理,在from子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有三个以上的表连接查询,那就需要选择交叉表作为基础表,交叉表是指被其它表所引用的表
  • WHERE子句中的连接顺序:Oracle采用自下而上的顺序解析where子句,根据这个原理,表之间的连接必须写在其它where条件之前,那些可以过滤掉最大数量记录的条件必须写在where子句的末尾

二、与内存相关的优化

1.UNION 操作符

  UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录。最常见的是过程表与历史表UNION。如:

select * from A union select * from B;

  这个sql在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。

  推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回:

select * from A union all select * from B;

2.SQL书写的影响

  同一功能同一性能不同写法sql的影响

  如,一个sql在A程序员写的为:

select * from employee;

  B程序员写为:

select * from scott.employee; (带表所有者的前缀)

  C程序员写为:

select * from EMPLOYEE; (大写表名)

  D程序员写为:

select *   from employee; (中间多了空格)

  以上四个sql在Oracle分析整理之后产生的结果及执行的时间是一样的,但是从Oracle共享内存SGA的原理,可以得出Oracle对每个sql都会对其进行一次分析,并且占用共享内存,如果将sql的字符串及格式写得完全相同则Oracle只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析sql的时间,而且也可以减少共享内存重复的信息,oracle也可以准确统计sql的执行频率。

3.避免在磁盘中排序

  当与Oracle建立起一个session时,在内存中就会为该session分配一个私有的排序区域。如果该连接是一个专用的连接(dedicated connection),那么就会根据init.ora中sort_area_size参数的大小在内存中分配一个Program Global Area(PGA)。如果连接是通过多线程服务器建立的,那么排序的空间就在large_pool中分配。不幸的是,对于所有的session,用作排序的内存量都必须是一样的,我们不能为需要更大排序的操作分配额外的排序区域。因此,设计者必须做出一个平衡,在分配足够的排序区域以避免发生大的排序任务时出现磁盘排序(disksorts)的同时,对于那些并不需要进行很大排序的任务,就会出现一些浪费。当然,当排序的空间需求超出了sort_area_size的大小时,这时将会在TEMP表空间中分页进行磁盘排序。磁盘排序要比内存排序大概慢14000倍。

  上面我们已经提到,私有排序区域的大小是由init.ora中的sort_area_size参数决定的。每个排序所占用的大小由init.ora中的sort_area_size参数决定。当排序不能在分配的空间中完成时,就会使用磁盘排序的方式,即在Oracle实例中的临时表空间中进行。

  磁盘排序的开销是很大的,有几个方面的原因。首先,和内存排序相比较,它们特别慢;而且,磁盘排序会消耗临时表空间中的资源。Oracle还必须分配缓冲池块来保持临时表空间中的块。无论什么时候,内存排序都比磁盘排序好,磁盘排序将会令任务变慢,并且会影响Oracle实例的当前任务的执行。还有,过多的磁盘排序将会令freebufferwaits的值特别高,从而令其它任务的数据块由缓冲中移走。

4.避免使用耗费资源的操作

  带有 DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的sql语句回启动sql引擎执行耗费资源的排序(SORT)功能。DISTINCT需要一次排序操作,而其它的至少需要执行两次排序。通常,带有UNION,MINUS,INTERSECT的sql语句都可以用其它方式重写。如果你的数据库的sort_area_size调配的好,使用UNION,MINUS,INTERSECT也是可以考虑的,毕竟它们的可读性很强。

三、其它性能优化相关技巧

1.删除重复记录

  最高效的删除重复记录方法(因使用了ROWID)例子:

delete from emp E where E.ROWID > (select MIN(X.ROWID) from emp X where X.emp_no = E.emp_no);

2.用 TRUNCATE 替代 DELETE

  当删除表中的记录时,在通常情况下,回滚段(rollback segments)用来存放可以被恢复的信息。如果你没有COMMIT事务,Oracle会将数据恢复到删除之前的状态(准确地说是恢复到执行删除之前的状态),而当运用TRUNCATE时,回滚段不再存放任何可被恢复的信息。当命令运行后,数据不能被恢复,因此很少的资源被调用,执行时间也会很短(TRUNCATE只在清空全表适用,TRUNCATE是DDL而不是DML)。

3.SELECT 子句中避免使用 *

  Oracle在解析的过程中,会将 * 依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。

4.用 WHERE 子句替换 HAVING 子句

  避免使用having子句,having只会在检索出所有记录之后才对结果集进行过滤,这个处理需要排序、总计等操作。如果能通过where子句限制记录的数目,那就能减少这方面的开销。sql语句中on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该是速度最快的,where也应该比having快点的,因为它过滤数据后才进行sum,在两个表连接时采用on,所以在一个表的时候,就剩下where跟having比较了。在单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确定的,where的作用时间是在计算之前就完成的,而having就是在计算之后才起作用的,所以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用,系统首先根据各个表之间的连接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完成后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在哪里。

5.使用表的别名(Alias)

  当在sql语句中连接多个表时,请使用表的别名并把别名前缀于每个column上。这样一来,就可以减少解析的时间并减少那些由column歧义引起的语法错误。

6.用 EXISTS 替代 IN、用 NOT EXISTS 替代 NOT IN

  在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。在这种情况下,使用EXISTS(或NOT EXISTS)通常将提高查询的效率。在子查询中NOT IN子句将执行一个内部的排序和合并。无论在哪种情况下,NOT IN都是最低效的(因为它对子查询中的表执行了一个全表遍历)。为了避免使用NOT IN,我们可以把它改写成外联接(OUTER JOIN)或NOT EXISTS。

  高效:

select * from emp where empno > 0 and EXISTS (select ‘X‘ from dept where dept.deptno = emp.deptno and loc = ‘MELB‘);

  低效:

select * from emp where empno > 0 and deptno IN (select deptno from dept where loc = ‘MELB‘);

7.用 EXISTS 替换 DISTINCT

  提交一个对多表信息(比如部门表和雇员表)进行查询的语句时,避免在select子句中使用distinct。一般可以考虑用EXISTS替换,EXISTS使查询更为迅速,因为RDBMS核心模块将在子查询的条件一但满足后,立刻返回结果。

  高效:

select dept_no, dept_name from dept D where EXISTS (select ‘X‘ from emp E where E.deptno = D.deptno);

  低效:

select DESTINCT dept_no, dept_name from dept D, emp E where D.deptno = E.deptno;

8.SQL语句使用大写

  因为Oracle总是先解析sql语句,把小写的字母转换成大写的再执行

9.用 >= 替代 >

  高效:

select * from emp where deptno >= 4;

  低效:

select * from emp where deptno > 3;

  两者的区别在于,前者DBMS将直接跳到第一个deptno等于4的记录,而后者将首先定位到deptno=3的记录并且向前扫描到第一个deptno大于3的记录。

10.优化 GROUP BY

  提高group by语句的效率,可以通过将不需要的记录在group by之前过滤掉。下面两个查询返回相同结果,但第二个就明显快了许多。

  低效:

select job, AVG(SAL) from emp group by job having job = ‘PRESIDENT‘ or job = ‘MANAGER‘;

  高效:

select job, AVG(SAL) from emp where job = ‘PRESIDENT‘ or job = ‘MANAGER‘ group by job;

原文地址:https://www.cnblogs.com/xiaogongjin/p/11828283.html

时间: 2024-10-12 22:29:32

Oracle数据库的sql语句性能优化的相关文章

SQL语句性能优化--LECCO SQL Expert

SQL语句的优化是将性能低下的SQL语句转换成目的相同的性能优异的SQL语句. 人工智能自动SQL优化就是使用人工智能技术,自动对SQL语句进行重写,从而找到性能最好的等效SQL语句. 数据库性能的优化   一个数据库系统的生命周期可以分成:设计.开发和成品三个阶段.在设计阶段进行数据库性能优化的成本最低,收益最大.在成品阶段进行数据库性能优化的成本最高,收益最小. 数据库的优化通常可以通过对网络.硬件.操作系统.数据库参数和应用程序的优化来进行.最常见的优化手段就是对硬件的升级.根据统计,对网

数据库SQL语句性能优化

选择最有效率的表名顺序 ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表. 如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表. WHERE子句中的连接顺序 ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WH

Oracle数据库常用Sql语句大全

一,数据控制语句 (DML) 部分 1.INSERT  (往数据表里插入记录的语句) INSERT INTO 表名(字段名1, 字段名2, --) VALUES ( 值1, 值2, --); INSERT INTO 表名(字段名1, 字段名2, --)  SELECT (字段名1, 字段名2, --) FROM 另外的表名; 字符串类型的字段值必须用单引号括起来, 例如: 'GOOD DAY' 如果字段值里包含单引号' 需要进行字符串转换, 我们把它替换成两个单引号''. 字符串类型的字段值超过

Oracle数据库入门——sql语句和函数详解

一.oracle常用数据类型 一.  数据定义语言(ddl) 数据定义语言ddl(data definition language)用于改变数据库结构,包括创建.更改和删除数据库对象. 用于操纵表结构的数据定义语言命令有: create table alter table truncate table drop table eg. --创建tb_stu表数据结构 create table tb_stu( id number, name varchar2(20) );   --修改tb_stu表数

Oracle 数据库常用sql语句及知识

<Oracle 数据库> 一.常用sql语句: 1.连接系统管理员账号:conn system/system(安装时口令); 2.创建新账号:create user Leo(账号名) identified by Leo(密码): 3.给新账号授权:grant connect,resource to Leo; 4.从某个账户收权:revoke resource from Leo; 5.连接新账号:conn Leo/Leo; 6.显示当前客户:show user; 7.查看表结构:desc s_e

oracle数据库入门sql语句

数据库: 命名规范问题 依然是 _流.看来也确实应该抽空 来处理一下 今天吧,不行时间不能浪费.要更加专注.累了 就睡,醒来 就 好好 弄东西.白天 哪怕累一些,强度 大一些,晚上也可以抽空出去.溜溜弯儿. 雷哥 问了一个 问题: Char 跟 varchar 有什么区别.如果 char 里面 定了4位.存了 一个 汉字,就是 两位.但是 在数据库空间依然是 4位.后面用空格补齐.但是 varchar 里面 假设定了 4位的长,存了一个 汉字 占了两位,这时候 在数据库中就只占2两位.所以 在空

oracle数据库之sql语句使用

SCOTT E-R模型图 DEPT 部门表             EMP 雇员表               BONUS 奖金表         GRADE 工资等级 DEPTNO 部门编号         EMPNO    雇员编号         ENAME 雇员名称     GRADE  工资等级 DNAME  部门名称        ENAME    雇员名称         JOB 工作职位          LOSAL   最低薪资 LOC    部门位置       JOB  

Oracle 数据库常用SQL语句(2)查询语句

一.SQL基础查询 1.select语句 格式:select 字段 from 表名; 2.where 用于限制查询的结果. 3.查询条件 > < >= <= = != 4.与 或(AND,OR) 5.在 不在(IN,NOT IN) 练习:查询工号为1,9,11,16且工资低于1000的员工. 6.在 [a,b] (between val1 and val2) 查询工资大于1000,小于1500的员工. select * from s_emp where salary between

解决oracle数据库删除sql语句出现^H字样

1:安装readline包 yum install readline* 2:安装源码包: rlwrap-0.30.tar.gz ./configure && make && make install 3:修改oracle用户的环境变量: alias sqlplus='rlwrap sqlplus' alias rman='rlwrap rman'