前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SQL优化法则小记

SQL优化法则小记

作者头像
赵腰静
发布2018-03-09 14:08:38
2K0
发布2018-03-09 14:08: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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-07-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据库SQL 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档