设为首页 收藏本站
查看: 757|回复: 0

[经验分享] 高效率常用的Oracle SQL语句

[复制链接]

尚未签到

发表于 2016-7-24 11:02:44 | 显示全部楼层 |阅读模式
  之一
  
  1. SELECT子句中避免使用 “*”


    当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 ‘*’
是一个方便的方法.不幸的是,这是一个非常低效的方法. 实际上,ORACLE在解析的过程中, 会将“*” 依次转换成所有的列名,
这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间.


2.使用DECODE函数来减少处理时间


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

Sql代码






  • SELECT
     
    COUNT
    (*),
    SUM
    (SAL) 
    FROM
     EMP 
    WHERE
     DEPT_NO = 0020 
    AND
      ENAME 
    LIKE
     ‘SMITH%’;  


  • SELECT
     
    COUNT
    (*),
    SUM
    (SAL) 
    FROM
     EMP 
    WHERE
     DEPT_NO = 0030 
    AND
     ENAME 
    LIKE
     ‘SMITH%’;  




SELECT COUNT(*),SUM(SAL) FROM EMP WHERE DEPT_NO = 0020 AND  ENAME LIKE ‘SMITH%’;
SELECT COUNT(*),SUM(SAL) FROM EMP WHERE DEPT_NO = 0030 AND ENAME LIKE ‘SMITH%’;

  你可以用DECODE函数高效地得到相同结果:

Sql代码






  • SELECT
     
    COUNT
    (DECODE(DEPT_NO,0020,’X’,
    NULL
    )) D0020_COUNT,  


  •         COUNT
    (DECODE(DEPT_NO,0030,’X’,
    NULL
    )) D0030_COUNT,  


  •         SUM
    (DECODE(DEPT_NO,0020,SAL,
    NULL
    )) D0020_SAL,  


  •         SUM
    (DECODE(DEPT_NO,0030,SAL,
    NULL
    )) D0030_SAL  


  • FROM
     EMP 
    WHERE
     ENAME 
    LIKE
     ‘SMITH%’;  




SELECT COUNT(DECODE(DEPT_NO,0020,’X’,NULL)) D0020_COUNT,
COUNT(DECODE(DEPT_NO,0030,’X’,NULL)) D0030_COUNT,
SUM(DECODE(DEPT_NO,0020,SAL,NULL)) D0020_SAL,
SUM(DECODE(DEPT_NO,0030,SAL,NULL)) D0030_SAL
FROM EMP WHERE ENAME LIKE ‘SMITH%’;

  类似的,DECODE函数也可以运用于GROUP BY 和ORDER BY子句中.


3.删除重复记录


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

Sql代码






  • DELETE
     
    FROM
     EMP E 
    WHERE
     E.ROWID > (
    SELECT
     
    MIN
    (X.ROWID) 
    FROM
     EMP X 
    WHERE
     X.EMP_NO = E.EMP_NO);  




DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO = E.EMP_NO);

  


4. 用TRUNCATE替代DELETE


    当删除表中的记录时,在通常情况下,回滚段(rollback segments )
用来存放可以被恢复的信息,如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况),而当运
用TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短.


5.计算记录条数


    和一般的观点相反, count(*) 比count(1)稍快 ,当然如果可以通过索引检索,对索引列的计数仍旧是最快的. 例如 COUNT(EMPNO)


6.用Where子句替换HAVING子句


    避免使用HAVING子句,HAVING 只会在检索出所有记录之后才对结果集进行过滤,这个处理需要排序、总计等操作,如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销, 例如:



Sql代码






  • --低效
      


  • SELECT
     REGION,
    AVG
    (LOG_SIZE) 
    FROM
     LOCATION 
    GROUP
     
    BY
     REGION 
    HAVING
     REGION REGION != ‘SYDNEY’ 
    AND
     REGION != ‘PERTH’  


  • --高效
      


  • SELECT
     REGION,
    AVG
    (LOG_SIZE)  
    FROM
      LOCATION 
    WHERE
     REGION REGION != ‘SYDNEY’ ND REGION != ‘PERTH’ 
    GROUP
     
    BY
     REGION  




--低效
SELECT REGION,AVG(LOG_SIZE) FROM LOCATION GROUP BY REGION HAVING REGION REGION != ‘SYDNEY’ AND REGION != ‘PERTH’
--高效
SELECT REGION,AVG(LOG_SIZE)  FROM  LOCATION WHERE REGION REGION != ‘SYDNEY’ ND REGION != ‘PERTH’ GROUP BY REGION

  


7. 用EXISTS替代IN


   在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率.

Sql代码






  • --低效
      


  • SELECT
     * 
    FROM
     EMP 
    WHERE
     EMPNO > 0 
    AND
     DEPTNO 
    IN
     (
    SELECT
     DEPTNO 
    FROM
     DEPT 
    WHERE
     LOC = ‘MELB’)  


  • --高效:
      


  • 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 FROM DEPT WHERE LOC = ‘MELB’)
--高效:
SELECT * FROM EMP WHERE EMPNO > 0 AND EXISTS (SELECT ‘X’  FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB’)

  


8.用NOT EXISTS替代NOT IN


   在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN都是最低效的
(因为它对子查询中的表执行了一个全表遍历).  为了避免使用NOT IN,我们可以把它改写成外连接(Outer Joins)或NOT
EXISTS. 例如:


SELECT …FROM EMP  WHERE DEPT_NO NOT IN (SELECT DEPT_NO FROM DEPT WHERE DEPT_CAT=’A’);

Sql代码






  • --为了提高效率改写为: (方法一: 高效)
      


  • SELECT
     ….
    FROM
     EMP A,DEPT B 
    WHERE
     A.DEPT_NO = B.DEPT(+) 
    AND
     B.DEPT_NO 
    IS
     
    NULL
     
    AND
     B.DEPT_CAT(+) = ‘A’  


  • -- (方法二: 最高效)
      


  • SELECT
     ….
    FROM
     EMP E 
    WHERE
     
    NOT
     EXISTS (
    SELECT
     ‘X’  
    FROM
     DEPT D 
    WHERE
     D.DEPT_NO = E.DEPT_NO 
    AND
     DEPT_CAT = ‘A’);  




--为了提高效率改写为: (方法一: 高效)
SELECT ….FROM EMP A,DEPT B WHERE A.DEPT_NO = B.DEPT(+) AND B.DEPT_NO IS NULL AND B.DEPT_CAT(+) = ‘A’
-- (方法二: 最高效)
SELECT ….FROM EMP E WHERE NOT EXISTS (SELECT ‘X’  FROM DEPT D WHERE D.DEPT_NO = E.DEPT_NO AND DEPT_CAT = ‘A’);

  


9.用EXISTS替换DISTINCT


    当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换


例如:

Sql代码






  • --低效: 
      


  • 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);  


  • --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);
--EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果.

  


10. 用索引提高效率


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

注:定期的重构索引是有必要的.


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


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

Sql代码






  • --低效:
      


  • SELECT
     …
    FROM
     DEPT 
    WHERE
     SAL * 12 > 25000;  


  • --高效:
      


  • SELECT
     … 
    FROM
     DEPT 
    WHERE
     SAL  > 25000/12;  




--低效:
SELECT …FROM DEPT WHERE SAL * 12 > 25000;
--高效:
SELECT … FROM DEPT WHERE SAL  > 25000/12;

  


12. 用>=替代>

Sql代码






  • --如果DEPTNO上有一个索引
      


  • --高效:
      


  •    SELECT
     *  
    FROM
     EMP  
    WHERE
     DEPTNO >=4     


  • --低效:
      


  •    SELECT
     *  
    FROM
     EMP   
    WHERE
     DEPTNO >3  




--如果DEPTNO上有一个索引
--高效:
SELECT *  FROM EMP  WHERE DEPTNO >=4   
--低效:
SELECT *  FROM EMP   WHERE DEPTNO >3

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

  
  最近在JavaEye上发现好多同志对sql的优化好像是知道的很少,最近总结了几条仅供参考,不过除少数可能要依情况而定,大多数还是相当有效的。


【注:以下说的(低效)与(高效)都是相当来说的。】



1、Where子句中的连接顺序:


ORACLE采用自下而上的顺序解析WHERE子句。


根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。



举例:


(低效)


select ... from table1 t1 where t1.sal > 300 and t1.jobtype =
'0001' and 20 < (select count(*) from table1 t2 where t2.pno =
t1.tno;



(高效)


select ... from table1 t1 where 20 < (select count(*) from
table1 t2 where t2.pno = t1.tno and t1.sal > 300 and t1.jobtype =
'0001';



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


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


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


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




3、减少访问数据库的次数:


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


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


由此可见,减少访问数据库的次数,就能实际上减少ORACLE的工作量。



举例:


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



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



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


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



举例:


(低效)


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'


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



6、删除重复记录:


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



举例:


delete from table1 t1


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



7、尽量不要使用having子句,可以考虑用where替换。


having只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作。


如果能通过where子句限制记录的数目,那就能减少这方面的开销。



8、尽量用表的别名:


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


这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。



9、用exists替代in(发现好多程序员不知道这个怎么用):


在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。


在这种情况下,使用exists(或not exists)通常将提高查询的效率。



举例:


(低效)


select ... from table1 t1 where t1.id > 10 and pno in (select no from table2 where name like 'www%');


(高效)


select ... from table1 t1 where t1.id > 10 and exists (select 1 from table2 t2 where t1.pno = t2.no and name like 'www%');



10、用not exists替代not in:


在子查询中,not in子句将执行一个内部的排序和合并。


无论在哪种情况下,not in都是最低效的 (因为它对子查询中的表执行了一个全表遍历)。


为了避免使用not in,我们可以把它改写成外连接(Outer Joins)或not exists。




11、用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核心模块将在子查询的条件一旦满足后,立刻返回结果.



12、用表连接替换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


(高效)


select ename from dept d, emp e where e.dept_no = d.dept_no and dept_cat = 'W';



13、避免在索引列上使用is null和is not null


避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引。


对于单列索引,如果列包含空值,索引中将不存在此记录;


对于复合索引,如果每个列都为空,索引中同样不存在此记录;


如果至少有一个列不为空,则记录存在于索引中。



举例:


如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null),


ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入),


然而如果所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空。


因此你可以插入1000 条具有相同键值的记录,当然它们都是空!


因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引。



14、最好把复杂的sql,去看下它的执行计划,这样有利于你分析知道自己的sql效率如何。



上面的资料也是本人长期看资料积攒下来的,并且很多都已经在项目中,特别是大数据量时得到了体现。

  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-248565-1-1.html 上篇帖子: Oracle中Number类型数据说明 下篇帖子: ORACLE EBS 的应用与实践
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表