Oracle SQL调优

Posted weishao-lsv

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle SQL调优相关的知识,希望对你有一定的参考价值。

在多数情况下,Oracle使用索引t来更快地遍历表,优化器主要根据定义的索引来提高性能。

但是,如果在SQL语句的where子句中写的SQL代码不合理,就会造成优化器删去索引而使用全表扫描,一般就这种SQL语句就是所谓的劣质SQL语句。

在编写SQL语句时我们应清楚优化器根据何种原则来删除索引,这有助于写出高性能的SQL语句

 

1. IS NULL 与 IS NOT NULL

不能用null作索引,任何包含null值的列都将不会被包含在索引中。

即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。

也就是说如果某列存在空值,即使对该列建索引也不会提高性能。

任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的

 

如果我们必须要用 is null,又需要提供查询效率 可以用函数索引

实例如下

create table test_date (name varchar2(20),day date);
insert into test_date(name ,day) values (lucy,null);
insert into test_date(name ,day) values (jony,null);
insert into test_date(name,day) values (james,sysdate);
select * from test_date;
--创建decode函数索引来代替 create index finx_day on test_date(decode(day,null,N, Y))

--使用decode判断来代替is null判断 select * from test_date a where decode(day,null,N,Y) = N

 

2. 联接列

对于有联接的列,即使最后的联接值为一个静态值,优化器是不会使用索引的。

假定有一个职工表(employee),对于一个职工的姓和名分成两列存放(FIRST_NAME和LAST_NAME),

现在要查询一个叫比尔.克林顿(Bill Cliton)的职工。

 

下面是一个采用联接查询的SQL语句,

select * from employs where 
first_name||’ ’||last_name =Beill Cliton

上面这条语句完全可以查询出是否有Bill Cliton这个员工,但是这里需要注意,系统优化器对基于last_name创建的索引没有使用。

当采用下面这种SQL语句的编写,Oracle系统就可以采用基于last_name创建的索引。

Select * from employee where
first_name =Beill and last_name =Cliton

 

 

如果一个变量(name)中存放着Bill Cliton这个员工的姓名,对于这种情况我们又如何避免全程遍历?

可以使用一个函数,将变量name中的姓和名分开就可以了,但是有一点需要注意,这个函数是不能作用在索引列上。‘

下面是SQL查询脚本

select * from employee
where
first_name = SUBSTR(&&name,1,INSTR(&&name, )-1)
and
last_name = SUBSTR(&&name,INSTR(&&name’, )+1)

 

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

以如下SQL讲解:

select * from employee where last_name like %cliton%

这里由于通配符(%)在搜寻词首出现,所以Oracle系统不使用last_name的索引。

在很多情况下可能无法避免这种情况,但是一定要心中有底,通配符如此使用会降低查询速度。

然而当通配符出现在字符串其他位置时,优化器就能利用索引。

在下面的查询中索引得到了使用:

select * from employee where last_name like c%

读者注意:项目真实开发中,如果经常性的模糊查询,可以采用solr或者elasticSearch或者直接Lucene也可以

 

4. Order by语句

ORDER BY语句决定了Oracle如何将返回的查询结果排序。

Order by语句对要排序的列没有什么特别的限制,也可以将函数加入列中(联接或者附加等)。

任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。

仔细检查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 <>INVALID

再看下面这个例子:

select * from employee where salary<>3000;

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

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

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

6. IN和EXISTS(下面有个重复的)

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

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

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

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

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

我相信绝大多数人会使用第一种格式,因为它比较容易编写,而实际上第二种格式要远比第一种格式的效率高。

在Oracle中可以几乎将所有的IN操作符子查询改写为使用EXISTS的子查询。

第二种格式中,子查询以‘select ‘X‘开始。运用EXISTS子句不管子查询从表中抽取什么数据它只查看where子句。

这样优化器就不必遍历整个表而仅根据索引就可完成工作(这里假定在where语句中使用的列存在索引)。

相对于IN子句来说,EXISTS使用相连子查询,构造起来要比IN子查询困难一些。

通过使用EXIST,Oracle系统会首先检查主查询,然后运行子查询直到它找到第一个匹配项,这就节省了时间。

Oracle系统在执行IN子查询时,首先执行子查询,并将获得的结果列表存放在在一个加了索引的临时表中。

在执行子查询之前,系统先将主查询挂起,待子查询执行完毕,存放在临时表中以后再执行主查询。这也就是使用EXISTS比使用IN通常查询速度快的原因。

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

 

7、Select子句中避免使用 “ * ”:

当你想在select子句中列出所有的column时,使用动态SQL列引用 ‘*‘ 是一个方便的方法。

不幸的是,这是一个非常低效的方法。

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

 

8、减少访问数据库的次数:

当执行每条SQL语句时,ORACLE在内部执行了许多工作:

解析SQL语句、估算索引的利用率、绑定变量、读数据块等等。

由此可见,减少访问数据库的次数,就能实际上减少ORACLE的工作量。

 

举例:

题目——我要查找编号为0001、0002学生的信息。

(低效)

select name,age,gender,address from t_student where id = 0001;
select name,age,gender,address from t_student where id = 0002;

(高效)

select a.name,a.age,a.gender,a.address,b.name,b.age,b.gender,b.address from t_student a,t_student b where a.id = 0001 and b.id = 0002;

 

9、使用Decode函数来减少处理时间:

使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表。

举例:

(低效)

select count(*), sum(banace) from table1 where dept_id = 0001 and name like anger%;
select count(*), sum(banace) from table1 where dept_id = 0002 and name like anger%;

 

(高效)

select count(decode(dept_id,0001,XYZ,null)) count_01,count(decode(dept_id,0002,XYZ,null)) count_02,
sum(decode(dept_id,0001,dept_id,null)) sum_01,sum(decode(dept_id,0002,dept_id,null)) sum_02
from table1
where name like anger%;

 

10、整合简单,无关联的数据库访问:

如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系)

举例:

(低效)

select name from table1 where id = 0001;
select name from table2 where id = 0001;
select name from table3 where id = 0001;

 

(高效)

    select t1.name, t2.name, t3.name
    from table1 t1, table2 t2, table3 t3
    where t1.id(+) = 0001 and t2.id(+) = 0001 and t3.id(+) = 0001

注:上面例子虽然高效,但是可读性差,需要量情而定啊!

 

11、删除重复记录:

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

举例:

delete from table1 t1
where t1.rowid > (select min(t2.rowid) from table1 t2 where t1.id = t2.id); 

 

12、尽量不要使用having子句,可以考虑用where替换:

having只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作。

如果能通过where子句限制记录的数目,那就能减少这方面的开销。

 

13、尽量用表的别名:

当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个Column上。

这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。

 

14、用exists替换distinct:

当提交一个包含一对多表信息的查询时,避免在select子句中使用distinct. 一般可以考虑用exists替换

举例:

(低效)

select distinct d.dept_no, d.dept_name from t_dept d, t_emp e where d.dept_no = e.dept_no;

(高效)

select d.dept_no, d.dept_name from t_dept d where exists (select 1 from t_emp where d.dept_no = e.dept_no);

exists使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果. 

 

15、用表连接替换exists:

通常来说,采用表连接的方式比exists更有效率。

举例:

(低效)

select ename from emp e where exists (select 1 from dept where dept_no = e.dept_no and dept_cat = W);

(高效)

select ename from dept d, emp e where e.dept_no = d.dept_no and dept_cat = W; 

 






以上是关于Oracle SQL调优的主要内容,如果未能解决你的问题,请参考以下文章

Oracle SQL调优记录

Oracle SQL调优

Oracle / SQL 调优顾问

sql Oracle调优查询

Oracle 查询/SQL 调优

Oracle SQL查询的性能调优