在多数情况下,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(),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(,INSTR()
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'

再看下面这个例子:

;

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

 or salary>;

虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许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学生的信息。

(低效)

';
';

(高效)

';

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

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

举例:

(低效)

' and name like 'anger%';
' and name like 'anger%';

(高效)

','XYZ',null)) count_02,
sum(decode(dept_id,',dept_id,null)) sum_02
from table1
where name like 'anger%';

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

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

举例:

(低效)

';
';
';

(高效)

    select t1.name, t2.name, t3.name
    from table1 t1, table2 t2, table3 t3
    '

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

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;

(高效)

 from t_emp where d.dept_no = e.dept_no);

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

15、用表连接替换exists:

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

举例:

(低效)

 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调优的更多相关文章

  1. Oracle SQL 调优健康检查脚本

    Oracle SQL 调优健康检查脚本 我们关注数据库系统的性能,进行数据库调优的主要工作就是进行SQL的优化.良好的数据架构设计.配合应用系统中间件和写一手漂亮的SQL,是未来系统上线后不出现致命性 ...

  2. Oracle SQL调优记录

    目录 一.前言 二.注意点 三.Oracle执行计划 四.调优记录 @ 一.前言 本博客只记录工作中的一次oracle sql调优记录,因为数据量过多导致的查询缓慢,一方面是因为业务太过繁杂,关联了太 ...

  3. Oracle SQL调优之分区表

    目录 一.分区表简介 二.分区表优势 三.分区表分类 3.1 范围分区 3.2 列表分区 3.3 散列分区 3.4 组合分区 四.分区相关操作 五.分区相关查询 附录:分区表索引失效的操作 一.分区表 ...

  4. Oracle SQL调优系列之SQL Monitor Report

    @ 目录 1.SQL Monitor简介 2.捕捉sql的前提 3.SQL Monitor 参数设置 4.SQL Monitor Report 4.1.SQL_ID获取 4.2.Text文本格式 4. ...

  5. Oracle SQL调优之表设计

    在看<收获,不止sql优化>一书,并做了笔记,本博客介绍一下一些和调优相关的表比如分区表.临时表.索引组织表.簇表以及表压缩技术 分区表使用与查询频繁而更新数据不频繁的情况,不过要记得加全 ...

  6. Oracle SQL 调优之 sqlhc

    SQL 执行慢,如何 快速准确的优化. sqlhc 就是其中最好工具之一 通过获得sql所有的执行计划,列出实际的性能的瓶颈点,列出 sql 所在的表上的行数,每一列的数据和分布,现有的索引,sql ...

  7. Oracle SQL调优之绑定变量用法简介

    目录 一.SQL执行过程简介 二.绑定变量典型用法 2.1.在SQL中绑定变量 2.2.在PL/SQL中使用绑定变量 2.3.PL/SQL批量绑定变量 2.4.Java代码里使用绑定变量 最近在看&l ...

  8. ORACLE SQL调优案例一则

    收到监控告警日志文件(Alert)的作业发出的告警邮件,表空间TEMPSCM2不能扩展临时段,说明临时表空间已经被用完了,TEMPSCM2表空间不够用了 Dear All:   The Instanc ...

  9. Oracle中SQL调优(SQL TUNING)之最权威获取SQL执行计划大全

    该文档为根据相关资料整理.总结而成,主要讲解Oracle数据库中,获取SQL语句执行计划的最权威.最正确的方法.步骤,此外,还详细说明了每种方法中可选项的意义及使用方法,以方便大家和自己日常工作中查阅 ...

随机推荐

  1. Linux系统中的硬件问题如何排查?(5)

    Linux系统中的硬件问题如何排查?(5) 2013-03-27 10:32 核子可乐译 51CTO.com 字号:T | T 在Linux系统中,对于硬件故障问题的排查可能是计算机管理领域最棘手的工 ...

  2. jquery easyui datagrid 远程加载数据----把主键渲染为值遇到的问题及解决方案

    起因:数据库中一些字段存的是代表具体值的数字,需要渲染为具体值 monggodb中的字典 mysql中存放的值为:expertin代表教练擅长的搏击技能 jquery easyui中的相关代码如下:用 ...

  3. IDEA 2018.1可用License服务(持续更新)

    1. http://idea.congm.in 2.http://idea.toocruel.net

  4. Help library 安装arcobjects for .NET异常问题

    新建一个reg文件写入,也可以导出一个reg文件在上面重新写入. Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\CLSID\{9DAA ...

  5. vue学习-day02(自定义指令,生命周期)

    目录: 1.案例:品牌管理    2.Vue-devtools的两种安装方式    3.过滤器,自定义全局或私有过滤器    4.鼠标按键事件的修饰符    5.自定义全局指令:让文本框获取焦点   ...

  6. Codeforces 919E Congruence Equation ( 数论 && 费马小定理 )

    题意 : 给出数 x (1 ≤ x ≤ 10^12 ),要求求出所有满足 1 ≤ n ≤ x 的 n 有多少个是满足 n*a^n  = b ( mod p ) 分析 : 首先 x 的范围太大了,所以使 ...

  7. POJ 1182 食物链 (带权并查集 && 向量偏移)

    题意 : 中文题就不说题意了…… 分析 : 通过普通并查集的整理归类, 能够单纯地知道某些元素是否在同一个集合内.但是题目不仅只有种类之分, 还有种类之间的关系, 即同类以及吃与被吃, 而且重点是题目 ...

  8. Prometheus 后续杂记

    在后续prometheus的使用中遇到的一些问题我会在此记录 搭建初期几个问题 rule.yml中对每条告警加上主机名? 要在告警通知中加上故障机器主机名不能从prometheus的采集监控项数据中的 ...

  9. Oracle-手工生成AWR

    运行awrrpt脚本 SQL> @?/rdbms/admin/awrrpt   Current Instance ~~~~~~~~~~~~~~~~      DB Id    DB Name   ...

  10. sqli-lab(13)

    Double Injection- String- with twist(双注入 - 字符型 - 变形) 来进行我们的注入测试 直接闭合,用户名和密码输入“ ‘) or (‘1’) = (‘1%23 ...