Oracle SQL优化

一、SQL优化的一般性原则

① 目标:减少服务器资源消耗(主要是磁盘I/O)

② 设计:尽量依赖Oracle优化器;创建合适的索引

③ 编码:利用索引;合理利用临时表;避免写过于复杂的sql; 尽量减小事务的粒度

二、注意事项

① 查询时尽量使用确定的列名;

② 少使用嵌套的子查询,这种查询很消耗cpu资源;

③ 多表查询的时候,选择最有效率的表名顺序(oracle解析器对表的处理顺序从右到左,所以记录少的表放在右边(最右边的表为基础表,drivering table最先被处理), 如果3个以上的表连接查询,则要选择交叉表作为基础表);

④ or 比较多时分为多个查询,使用union all(尽量用union all代替union)联结;

⑤ 尽量多用commit提交事务,可以及时释放资源、解锁、释放日志;

⑥ 访问频繁的表可以放置在内存中;

⑦ 避免复杂的多表关联;

⑧ 避免distinct,union(并集),minus(差集),intersect(交集),order by等耗费资源的操作,因为会执行耗费资源的排序功能;

⑨ 使用exists替代distinct;

⑩ 删除全表时利用truncate代替delete(delete删除时,没有commit前可以回滚;truncate后不能回滚,执行时间较短);

⑪ 使用表的别名,可以减少解析时间;

⑫ exists和in的选择问题,不同时候区分对待;

⑬ 合理使用索引。

三、SQL语句的执行顺序

⑧ SELECT   ⑨DISTINCT (11)  

① FROM   ③ JOIN ② ON

④ WHERE   ⑤ GROUP BY   ⑥ WITH {CUBE | ROLLUP}   ⑦ HAVING  

⑩ ORDER BY

描述:

① FROM:对FROM子句中的前两个表执行笛卡尔积(交叉联接),生成虚拟表VT1。

② ON:对VT1应用ON筛选器,只有那些使为真才被插入到TV2。

③ OUTER (JOIN):如果指定了OUTER JOIN(相对于CROSS JOIN或INNER JOIN),保留表中未找到匹配的行将作为外部行添加到VT2,生成TV3。如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。(是不是说除OUTER JOIN,其他JOIN都是跟相当于和ON同时执行,不会先计算笛卡尔积,或者是说先是JOIN,然后ON)

④ WHERE:对TV3应用WHERE筛选器,只有使为true的行才插入TV4。

⑤ GROUP BY:按GROUP BY子句中的列列表对TV4中的行进行分组,生成TV5。

⑥ CUTE | ROLLUP:把超组插入VT5,生成VT6。

⑦ HAVING:对VT6应用HAVING筛选器,只有使为true的组插入到VT7。

⑧ SELECT:处理SELECT列表,产生VT8。

⑨ DISTINCT:将重复的行从VT8中删除,产品VT9。

⑩ ORDER BY:将VT9中的行按ORDER BY子句中的列列表顺序,生成一个游标(VC10),生成表TV11,并返回给调用者。

四、SQL语句

1、union 和 union all

① union 多用于合并两个或者多个 select 的结果集。

② union 会去掉重复的结果,union all 会保存 select 查出来的所有结果。

③ union 内部的 select 结果集必须拥有相同数量的列,列的顺序必须相同;列也必须是相同的数据类型,像 '111' 和 111 这样的结果集也不能合并,会报 “ORA-00900:无效SQL语句”。

④ union 结果集的列明等于第一个 select 结果集的列名。

2、where 和 having

① where 不能放在 group by 后面。

② where 后面的条件不能有聚合函数。比如 sum()。

③ having 是跟 group by 放在一起使用的,放在 group by 的后面,作用相当于 where 。

④ where 和 having 都是对查询结果的一种筛选。

⑤ where 字句用于从 from 返回的值。where 子句中可以使用比较运算符(>、>=、<、<=、=、<>、!=)和逻辑运算符(and、or、not)。from 返回的每一行数据都会用 where 字句中的条件进行筛选。

⑥ having 对 group by 的结果进行进一步的筛选。在查询过程中 聚合函数 比 having 语句优先执行。where 字句优先于聚合函数执行。having 可以对 group by 、where、from 的返回结果进行筛选。

3、exists 和 in

1 select * from student a where pro_id in (select pro_id from stu_pro) order by a.id;
2 select * from student a where exists (select 1 from stu_pro b where a.pro_id=b.pro_id) order by a.id;
3 select * from student a,stu_pro b where a.pro_id=b.pro_id order by a.id;

① exists 对外表做循环,每次循环对内表进行查询;in 将内表和外表进行hash连接。

② 使用 exists  Oracle会先检查主查询;使用in,优先执行子查询,并将结果存到内存中。

③ 如上述代码,in使用的是A表的索引,exists使用的是B的索引,C同时使用A和B表的索引。

④ 尽量不要使用not in。

⑤ 结论:如果A和B的数据量不大,那么三条语句的执行效率相差不大;如果A表大,B表小,使用in;如果B表大,A表小,使用exists;无论如何,第三行语句单扫描次数是笛卡尔乘积,效率最差。

4、with as

语法

with tempName as (select ....)
select ...
--多个with table as 一起使用时用逗号隔开,并且只能使用一个with
with tempName1 as (select ....),
tempName2 as (select ....),
tempName3 as (select ....)
select ...

① 在真正进行查询之前预先构造了一个临时表TT,之后便可多次使用它做进一步的分析和处理;

② 对于复杂查询,使用with table as可以抽取公共查询部分,多次查询时可以提高效率;

③ 使用子查询的方法表被扫描两次,而使用WITH Clause方法,表仅被扫描一次。这样可以大大的提高数据分析和查询的效率。

④ 增加了代码的易读性。

5、delete 和 truncate

① 两者都可以用来删除表中所有的记录;

② truncate是DDL操作,它移动HWK,不需要 rollback segment ;

③ 而Delete是DML操作, 需要rollback segment 且花费较长时间;

④ 介于上面三点,全表删除时使用 truncate。

6、绑定变量(bind variable)

绑定变量是查询中的一个占位符:如&variable。

例如要获取 student 表中的 stu_id = ‘0001’ 的学生信息:

① select * from student where stu_id = ‘0001’;

② 先绑定变量&stu_id,&stu_id 的值设为 ‘0001’,再执行查询 select * from student where stu_id = &stu_id;

第一个查询直接使用 ‘0001’ 这个常量,如果有多个这样的查询,则每一个查询对数据库来说都是一个全新的查询。Oracle每次都会对查询进行解析,限定(命名解析)、安全性检查、优化等(简单来说,就是每次执行前都要先编译)。

第二个查询使用了&stu_id这样一个绑定变量,变量在执行时动态指定,这个查询只会在第一次时编译,随后oralce会把查询计划存储在一个共享池中方便以后重用,以后在传入&stu_id不同的值进行查询时,Oracle会使用第一次解析好的这个查询计划执行,查询效率大幅提升。

7、null 值、'' 空字符串、空格

① 空值等价于没有任何值,是未知数。Oracle中 null 和 '' (空字符串)是一个意思,不能使用 = '' 作为查询条件,只能使用is null,不为空为is not null。

② 对 null 值做加、减、乘、除等运算操作,结果仍为空。

③ 控制可以进行拼接字符串操作,结果正常。

④ 空值的处理使用 nvl 函数,给null值赋值后在进行使用。

⑤ 空值排序时比其他数据都大(索引默认是降序排列),所以null值总是排在最后。

⑥ 空格当做正常的字符串处理,不能插入到数值类型列中,排序时排在最前面。

insert into student (stu_id, stu_name, sex, credit) values ('0012', '一一', ' ', '79');
insert into student (stu_id, stu_name, sex, credit) values ('0013', '二二', '', '55');
insert into student (stu_id, stu_name, sex, credit) values ('0014', '三三', null, '76');

insert into student (stu_id, stu_name, sex, credit) values ('0015', '四四', '', ' ');--插入报错,数值类型不能插入空格

--空值和空字符串是一样的
select * from student; --查询到 3 条记录
select * from student where sex =''; --查询到 0 条记录
select * from student where sex is null; --查询到 2 条记录
--空值不能被索引
select count(sex) from student where stu_id ='0014';--查询到 0 条记录
--空值需要nvl处理后再查
select count(nvl(sex,0)) from student where stu_id ='0014';--查询 到 1 条记录

--空值排序时候排在最后、空格排在最前
select * from student order by sex;

--使用降序将空值排到最前,此时,空格排在最后
select * from student order by sex desc;

--使用nulls first (不管asc或者desc,null记录排在最前)或者nulls last 可以控制null的位置。不影响空格位置
select * from student order by sex nulls first;
原文地址:https://www.cnblogs.com/wangrui1587165/p/9268798.html