SQL优化法则小记

时间:2023-02-02 18:16:38

SQL优化技巧

1.选择最有效率的表名顺序(只在基于规则的优化器中有效):

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

2.where子句中的连接顺序:

oracle 采用自下而上的顺序解析where子句,根据这个原理,表之间的连接必须写 在其他where条件之前, 那些可以过滤掉最大数量记录的条件必须写在where子句的末尾.

3.select子句中避免使用 ‘ * ‘:

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

4.减少访问数据库的次数:

oracle在内部执行了许多工作: 解析 SQL 语句, 估算索引的利用率, 绑定变量,读数据块等

5.在 SQL*Plus , SQL*Forms 和 Pro*C 中重新设置arraysize参数, 可以增加每 次数据库访问的检索数据量 ,建议值为200

6.使用 decode函数来减少处理时间:

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

7.删除重复记录:

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

delete from  emp e  where

e.rowid > (select min(x.rowid)

from emp x where x.emp_no=e.emp_no);

8.整合简单,无关联的数据库访问:

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

9.用truncate替代delete:

当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息.

如果你没有commit事务,oracle会将数据恢复到删除之前的状态(准确 地说是恢复到执行删除命令之前的状况) 而当运用truncate时, 回滚段不再存放 任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时 间也会很短. (译者按: truncate只在删除全表适用,truncate 是 DDL 不是 DML) .

10.尽量多使用 commit:

只要有可能,在程序中尽量多使用 commit, 这样程序的性能得到提高,

需求也会因为 commit所释放的资源而减少:

commit所释放的资源:

a. 回滚段上用于恢复数据的信息.

b. 被程序语句获得的锁

c. redo log buffer 中的空间

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

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

12.减少对表的查询:

在含有子查询的 SQL 语句中,要特别注意减少对表的查询.例子:

select tablename from tables

where (tab_name,db_ver)=(select tab_name,db_ver from tab_colimns

where version =604)

13.通过内部函数提高 SQL 效率.:

复杂的 SQL 往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际 工作中是非常有意义的 .

14.使用表的别名(Alias):

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

15.用 exists替代 in、用 not exists 替代not in:

在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接,在这种情况下, 使用exists(或not exists )通常将提高查询的效率. 在子查询中,not in子句将执行一个内部的排序和合并. 无论在哪种情况下,not in都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用 not in ,我们可以把它改写成 外连接(Outer Joins)或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 dept where loc='melb')

16.识别'低效执行'的 SQL 语句:  虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己的SQL工具来解决 问题始终是一个最好的方法:

select  executions , disk_reads, buffer_gets,

round((buffer_gets-disk_reads)/buffer_gets,2) hit_radio,

round(disk_reads/executions,2) reads_per_run,

sql_text  from  v$sqlarea  where

executions>0  and  buffer_gets > 0  and

(buffer_gets-disk_reads)/buffer_gets < 0.8

order by  4 desc;

17.用索引提高效率:

索引是表的一个概念部分,用来提高检索数据的效率,oracle使用了一个复杂的自 平衡B-tree结构. 通常,通过索引查询数据比全表扫描要快. 当oracle找出执行查询 和 update 语句的最佳路径时, oracle优化器将使用索引. 同样在联结多个表时使用 索引也可以提高效率. 另一个使用索引的好处是,它提供了主键(primary key)的唯一 性验证.。那些 long或long raw数据类型, 你可以索引几乎所有的列. 通常, 在大 型表中使用索引特别有效. 当然,你也会发现, 在扫描小表时,使用索引同样能提高效 率. 虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价. 索引需 要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时, 索引本 身也会被修改. 这意味着每条记录的 insert delete update将为此多付出 4 , 5 次 的磁盘 I/O . 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询 反应时间变慢.。定期的重构索引是有必要的:  alter index <indexname> rebuild <tablespacename>

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

例子:

(低效):

select  distinct  dept_no,dept_name  from  dept d , emp e  where

d.dept_no = e.dept_no

(高效):

select dept_no,dept_name

from  dept d

where

exists ( select ‘x'  from emp e where e.dept_no = d.dept_no);

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

20.在 java 代码中尽量少用连接符“+”连接字符串!

21.避免在索引列上使用 not通常,   我们要避免在索引列上使用 not, not会产生在和在索引列上使用函数相同的影响. 当 oracle”遇到”not,他就会停止使用索引转而执行全表扫描.

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

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

低效:

select … from  dept  where sal * 12 > 25000;

高效:

select … from dept where sal > 25000/12;

23.用>=替代>

高效:  select * from  emp  where  deptno >=4

低效:  select * from emp where deptno >3

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

24.用 union替换 or(适用于索引列)  通常情况下, 用 union 替换 where子句中的 or将会起到较好的效果. 对索引列使用or将造成全表扫描. 注意, 以上规则只针对多个索引列有效. 如果有column没有 被索引, 查询效率可能会因为你没有选择 or而降低. 在下面的例子中, loc_id和 region上都建有索引.

高效:

select loc_id , loc_desc , region  from location

where loc_id = 10

union

select loc_id , loc_desc , region

from location

where region = “melbourne”

低效:

select loc_id , loc_desc , region  from location

where loc_id = 10 or region = “melbourne”

如果你坚持要用 or, 那就需要返回记录最少的索引列写在最前面.

25.用 in来替换or 这是一条简单易记的规则,但是实际的执行效果还须检验,两者的执行路径似乎是相同的.

低效:

select…. from location where loc_id = 10 or loc_id = 20 or loc_id = 30

高效

select… from location where loc_in  in (10,20,30);

26.避免在索引列上使用is null和is not null避免在索引中使用任何可以为空的列,oracle将无法使用该索引.对于单列索引, 如果列包含空值,索引中将不存在此记录. 对于复合索引,如果每个列都为空,索 引中同样不存在此记录. 如果至少有一个列不为空,则记录存在于索引中.举例: 如 果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的 A,B 值为 (123,null) , oracle将不接受下一条具有相同 A,B 值(123,null)的记录(插入). 然而如果所有的索引列都为空,oracle将认为整个键值为空而空不等于空. 因此你可以插 入 1000 条具有相同键值的记录,当然它们都是空! 因为空值不存在于索引列中,所以 where子句中对索引列进行空值比较将使 oracle停用该索引.

低效: (索引失效)

select … from  department  where  dept_code is not null;

高效: (索引有效)

select … from  department  where  dept_code >=0;

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

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

28.用 union-all替换 union( 如果有可能的话):  当 SQL 语句需要 union 两个查询结果集合时,这两个结果集合会以 union-all的方 式被合并, 然后在输出最终结果前进行排序. 如果用 union-all替代union, 这样排 序就不是必要了. 效率就会因此得到提高. 需要注意的是, union-all 将重复输出两 个结果集合中相同记录. 因此各位还是要从业务需求分析使用  union-all 的可行性. union将对结果集合排序,这个操作会使用到 sort_area_size 这块内存. 对于这块 内存的优化也是相当重要的. 下面的 SQL 可以用来查询排序的消耗量.

低效:

select  acct_num, balance_amt

from  debit_transactions

where tran_date = '31-dec-95'

union

select acct_num, balance_amt

from debit_transactions

where tran_date = '31-dec-95'

高效:

select acct_num, balance_amt

from debit_transactions

where tran_date = '31-dec-95'

union all

select acct_num, balance_amt

from debit_transactions

where tran_date = '31-dec-95'

29.用 where 替代order by:

order by 子句只在两种严格的条件下使用索引.

order by 中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.

order by中所有的列必须定义为非空.

where 子句使用的索引和order by子句中所使用的索引不能并列.

例如:

表 dept包含以下列:

dept_code pk not null  dept_desc not null  dept_type null

低效:

(索引不被使用)

select dept_code from dept order by  dept_type

高效:

(使用索引)

select dept_code  from  dept  where  dept_type > 0

30.避免改变索引列的类型:

当比较不同数据类型的数据时, ORACLE 自动对列进行简单的类型转换.  假设 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  whereto_number(emp_type)=123

因为内部发生的类型转换, 这个索引将不会被用到!

为了避免oracle对你的SQL进 行隐式的类型转换, 最好把类型转换用显式表现出来. 注意当字符和数值比较时, oracle会优先转换数值类型到字符类型

31.需要当心的 where子句:  某些 select语句中的 where子句不使用索引.

这里有一些例子.  在下面的例子里,

(1)‘!=' 将不使用索引. 记住, 索引只能告诉你什么存在于表中, 而 不能告诉你什么不存在于表中.

(2) ‘||'是字符连接函数. 就象其他函数那样, 停用了 索引.

(3) ‘+'是数学函数. 就象其他数学函数那样, 停用了索引.

(4)相同的索引列不能 互相比较,这将会启用全表扫描.

32.

a. 如果检索数据量超过 30%的表中记录数.使用索引将没有显著的效率提高.

b. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别. 而通常情况下,使用索引比全表扫描要块几倍乃至几千倍!

33.避免使用耗费资源的操作:  带有 distinct,union,minus,intersect,order by 的 SQL 语句会启动 SQL 引擎  执行耗费资源的排序(SORT)功能. distinct 需要一次排序操作, 而其他的至少需要执 行两次排序. 通常, 带有union,minus,intersect 的 SQL 语句都可以用其他方式重 写. 如果你的数据库的 sort_area_size 调配得好, 使用union,minus,intersect 也是可以考虑的, 毕竟它们的可读性很强

34.优化 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