Oracle SQL调优

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

可是,若是在SQL语句的where子句中写的SQL代码不合理,就会形成优化器删去索引而使用全表扫描,通常就这种SQL语句就是所谓的劣质SQL语句。函数

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

 

1. IS NULL 与 IS NOT NULL

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

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

也就是说若是某列存在空值,即便对该列建索引也不会提升性能。code

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

 

若是咱们必需要用 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查询效率更高

 

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

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

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

实际上,ORACLE在解析的过程当中,会将 '*' 依次转换成全部的列名, 这个工做是经过查询数据字典完成的, 这意味着将耗费更多的时间。

 

八、减小访问数据库的次数:

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

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

因而可知,减小访问数据库的次数,就能实际上减小ORACLE的工做量。

 

举例:

题目——我要查找编号为000一、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';

 

九、使用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%';

 

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

若是你有几个简单的数据库查询语句,你能够把它们整合到一个查询中(即便它们之间没有关系)

举例:

(低效)

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'

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

 

十一、删除重复记录:

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

举例:

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

 

十二、尽可能不要使用having子句,能够考虑用where替换:

having只会在检索出全部记录以后才对结果集进行过滤. 这个处理须要排序,总计等操做。

若是能经过where子句限制记录的数目,那就能减小这方面的开销。

 

1三、尽可能用表的别名:

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

这样一来,就能够减小解析的时间并减小那些由Column歧义引发的语法错误。

 

1四、用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核心模块将在子查询的条件一旦知足后,马上返回结果. 

 

1五、用表链接替换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'; 
相关文章
相关标签/搜索