Oracle SQL语句优化

年底五花八门的报表统计,纷繁杂乱的数据采集,我想你是懂的。可一些让你我蛋疼的需求往往会让人求生不得、求死不能地跑上个四五小时,甚至一两天。这时我们需要对SQL语句、库表进行优化。所谓语句优化,就是在SQL语句中运用语句技巧、表索引、性能分析工具等方法使得语句能在原基础上更快速地得到结果的方法。以下为工作中积攒的以及网络上搜集的一些SQL优化经验,与君分享、以兹鼓励、共同进步。

1、在查询中尽量不使用“*”

Oracle在执行SQL语句时,会用表中的所有列名替代“*”,这样就浪费了很多查询时间。所以,在使用“*”进行查询时,要考虑是否有字段是不需要的,如果有不需要的字段就要用制定列名的方式查询,这样就能够提高查询速度。

说明:SQL查询语句执行步骤:首先执行FROM子句,形成一个中间表(临时表);然后执行WHERE子句,去除不满足该关系的记录;最后执行SELECT子句,提取(或替代“*”)所需要的字段。

2、多表查询时多使用别名

实现多表查询时,应该考虑使用别名代替原表名,并在查询字段前面加上别名作为前缀,这样Oracle数据库在执行这样的查询语句时,就能很清楚地判断出哪些字段来自于哪个表,减少不必要的解析时间。

说明:在Oracle数据中设置表的别名方式主要有下面两种:“SELECT * FROM 表名 别名”;“SELECT * FROM 表名 AS 别名”。除了多表查询时给表设置别名外,在表的顺序上也要注意表的关联关系,因为在Oracle数据库中,查询时也是按表出现的顺序进行链接的。

3、条件查询多使用WHERE,避免使用HAVING

在SQL语言中指定查询条件时主要有两个关键词:一个是WHERE子句,另一个是HAVING子句。但是在实际的应用中,应该尽量避免使用HAVING子句,因为HAVING子句通常用于在检索出的结果集中过滤结果,所以效率会比较底。使用WHERE子句可以在查询时就限定HAVING这样就可以提高查询速度。

4、计算时不用索引列

在一个表中设置索引列是为了提高表的检索速度,但是要对索引列的值进行计算,那么,检索这个表时索引列就会失去效果,也就是说,在查询数据时索引列不会起作用,而是对表进行了全表扫描。因此,当在查询语句中要对列进行计算时,就必须要看清楚再计算中是否使用了索引列,尽量避免使用索引列进行计算,这样就能达到优化查询语句的目的。

5、指定查询范围时多使用IN

当要查询某个值在某个范围时,使用“OR”表示“或者”,使用“AND”表示“并且”,但是IN关键词也可以表示与OR关键词相同的意义。相对于OR关键词来说,使用IN关键词的查询效率更高一些,OR关键词要比较每一个条件,所以检索速度就慢了。

6、使用TRUNCATE清空表中记录

在删除表中的记录时通常会使用DELETE关键词进行操作,但是在表中记录全部删除时还有一个优秀的关键词可以选择,这就是TRUNCATE关键词。那么,使用TRUNCATE和DELETE有什么区别呢?最主要的一个区别就是DELETE属于数据操纵语言(DML),而TRUNCATE关键词则属于数据定义语言(DDL)。正因为他们的这个区别,在执行DELETE操作后,数据库会把DELETE操作之前的结果存到回滚段中,当错误删除记录时,可以通过事务中回滚操作恢复到DELETE操作前状态,而使用TRUNCATE删除记录时,是不会把信息放到回滚段的,删除后记录就不能够再恢复了。

因此,使用TRUNCATE操作的效率要比使用DELETE操作的效率更高。但是,唯一不足的就是TRUNCATE语句只能删除表中全部记录,而使用DELETE语句可以有选择地删除表中的记录。

7、及时提交事务

当使用一个事务时,不要在执行完整个语句块再提交事务,而是在执行完某一个小功能时就提交事务,及时提交事务的好处有如下几点:

a. 及时清空回滚段中的内容

b. 及时释放程序中因事务而锁定的资源

c. redo log buffer 中的空间 

d. ORACLE为管理上述3种资源中的内部花费

8、多使用EXISTS语句判断条件

EXISTS表示存在的意思,与其相反的是NOT EXISTS。这个关键词可以替代SQL语句中很多关键词,比较典型的就是使用EXISTS判断是否存在,可以用来替代IN关键词;EXISTS还可以替代DISTINCT关键词来查询。使用EXISTS关键词查询数据比使用IN关键词和DISTINCT关键词效率要高,使用EXISTS替代DISTINCT的情况主要是一对多表查询时使用。

用IN写出来的SQL的优点是比较容易写及清晰易懂,但是用IN的SQL性能总是比较低的,从Oracle执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别:Oracle试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。

Oracle在执行IN子查询时,首先执行子查询,将查询结果放入临时表再执行主查询。而EXIST则是首先检查主查询,然后运行子查询直到找到第一个匹配项。NOT EXISTS比NOT IN效率稍高。但具体在选择IN或EXIST操作时,要根据主子表数据量大小来具体考虑。

说明:无论是使用EXISTS还是IN对于多个表的查询,都没有直接对两个表进行关联查询效率高。

9、避免在索引列上使用NOT

要避免在索引列上使用NOT,NOT会产生在和在索引列上使用函数相同的影响.。当ORACLE“遇到”NOT,他就会停止使用索引转而执行全表扫描。

10、用“>=”替代“>”(“<=”替代“<”)

高效: SELECT * FROM  EMP  WHERE  DEPTNO >=4 

低效: SELECT * FROM EMP WHERE DEPTNO > 3 

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

11、 使用大写字母代替小写字母

在编写SQL语句时,虽然在数据库中是不区分大小写的,但是在执行SQL语句时,数据库首先会把语句中所有的小写字母转换成大写字母。所以,在编写SQL语句时尽量使用大写字母,这样能够提高数据库识别语句的速度。

12、尽量不使用不等号(“!=”,“<>")

在进行比较时经常会用到不等号,不等号有两种写法,即“!=”和“<>”。使用不等号就相当于不会使用到索引,也就是说,使用不等号会对整个表进行扫描,这就降低了查询效率。所以,尽可能使用其他方式代替不等号,比如可以使用“>”或“〈”号代替。

13、 尽量不使用NULL关键词判断空

当使用IS NULL判断字段是否为空或者IS NOT NULL判断字段非空时,查询数据就不能使用表中的索引查询了,这就降低了查询效率。应该使用其他方式代替NULL关键词,比如,在设计数据库时就不允许填入空值,或者用默认值代替空值等。

14、使用UNION ALL关键词

UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果,如果表数据量大的话可能会导致用磁盘进行排序。实际大部分应用中是不会产生重复的记录,所以采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。

 

小结:

在前面的优化中多次提到了不要破坏表中已经设置的索引,如果破坏了索引就会影响查询效率。所以,为表设置索引也是SQL优化的一个主要工作。其实,也并不是每个表都必须要使用索引,通常当要查询表中的2%-4%数据时,可以为表创建索引,这样能显著地提高查询速度。也就是说,建立索引是为了查询,如果在实际的查询中并没有用到索引,那么就没有必要强制创建索引。在为表创建索引时,还要考虑为哪些列创建索引合适。其实,也就是为查询条件中用到的列设置索引效果更好一些。

 

补充一:{可能引起全表扫描的操作}

  1. 对索引列上使用NOT或者“<>”
  2. 对索引列使用函数或者计算
  3. 通配符位于查询字符串的第一个字符
  4. IS NULL或者IS NOT NULL
  5. 多列索引,但它的第一个列并没有被WHERE子句引用

补充二:{避免使用消耗资源的操作}

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

补充三:{SQL字符串格式化}

从ORACLE共享内存SGA的原理,可以得出Oracle对每个SQL 都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同则Oracle只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,Oracle也可以准确统计SQL的执行频率。所以,不同区域出现的相同的SQL语句,要保证查询字符串完全相同,以利用SGA共享池,防止相同的SQL语句被多次分析。

补充四:{WHERE后面的条件顺序}

Oracle从下到上处理Where子句中的多个查询条件,所以表连接语句应写在其他WHERE条件前,可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾

补充五:{FROM后面的表顺序}

Oracle的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询,那就需要选择交叉表作为基础表,交叉表是指那个被其他表所引用的表。

你可能感兴趣的:(oracle优化)