长沙做网站公司解密如何编写高效率的SQL语句

如何书写高效的SQL语句
 长沙做网站公司在应用开发中,书写SQL语句是最基本的工作,但我们往往在这方面侧重的是功能的实现,很容易把效率问题给忽略了,在随着系统数据的不断增加,可能有些低效的SQL语句会让客户感觉系统反映缓慢,甚至还有可能会因长时间执行不出结果而报超时或其它系统错误,所以我们要养成一个良好的编码习惯,不仅要实现其功能,而且要尽可能的提高效率。
 下面内容是在网络上收集的一些比较典型实用的优化点,以及在工作中用到的典型样例,供大家参考学习。

1、注意UNion和UNion all 的区别。
   它们两都可以把相同结果集并在一起,但UNION all的效率稍好一些,在就是如果A并上B有重复记录,union all可以把重复数据distict掉

2、注意使用DISTINCT,在没有必要时不要用,它同UNION一样会使查询变慢。重复的记录在查询里是没有问题的 

3、查询时不要返回不需要的行、列 ,即最好不要用select * from tablename,这样会把所有的字段都解析出来.

4、如果使用了IN或者OR等时发现查询没有走索引,使用显示申明指定索引:   
   SELECT   *   FROM   PersonMember   (INDEX   =   IX_Title)   WHERE   processid   IN   (‘男’,‘女’) 

5、在SQL2000以前,一般不要用如下的字句:   "IS   NULL",   " <> ",   "!=",   "!> ",   "! <",   "NOT",   "NOT   EXISTS",   "NOT   IN",   "NOT   LIKE",   and   "LIKE%500’",因为他们不走索引全是表扫描。也不要在WHere字句中的列名加函数,如Convert,substring等,如果必须用函数的时候,创建计算列再创建索引来替代.还可以变通写法:WHERE   SUBSTRING(firstname,1,1)   =   ’m’改为WHERE   firstname   like  ’m%’(索引扫描),一定要将函数和列名分开。并且索引不能建得太多和太大。NOT   IN会多次扫描表,使用EXISTS、NOT   EXISTSIN   ,   LEFT   OUTER   JOIN   来替代,特别是左连接,而Exists比IN更快,最慢的是NOT操作.如果列的值含有空,以前它的索引不起作用,现在2000的优化器能够处理了。相同的是IS   NULL,“NOT",   "NOT   EXISTS",   "NOT   IN"能优化她,而” <> ”等还是不能优化,用不到索引。 对于like的高效使用方法 本站有详细的讲解。

6、将需要查询的结果预先计算好放在表中,查询的时候再SELECT。这在SQL7.0以前是最重要的手段。例如医院的住院费计算。 

7MIN() 和 MAX()能使用到合适的索引 

8、数据库有一个原则是代码离数据越近越好,所以优先选择Default,依次为Rules,Triggers,   Constraint(约束如外健主健CheckUNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不仅维护工作小,编写程

序质量高,并且执行的速度快。 

 9、如果要插入大的二进制值到Image列,使用存储过程,千万不要用内嵌INsert来插入(不知JAVA是否)。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储

过程就没有这些动作:   方法:Create   procedure   p_insert   as   insert   into   table(Fimage)   values   (@image),   在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。 

10、Between在某些时候比IN速度更快,Between能够更快地根据索引找到范围。用查询优化器可见到差别。   select   *   from   chineseresume   where   title   in   (’男’,’女’)   Select   *   from  

chineseresume   where   between   ’男’   and   ’女’   是一样的。由于in会在比较多次,所以有时会慢些。 

11、在必要是对全局或者局部临时表创建索引,有时能够提高速度,但不是一定会这样,因为索引也耗费大量的资源。他的创建同是实际表一样。 

12、不要建没有作用的事物例如产生报表时,浪费资源。只有在必要使用事物时使用它。  
       
13、用OR的字句可以分解成多个查询,并且通过UNION   连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用UNION   all执行的效率更高.多个OR的字句没有用到索引,改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。 

14、对于长沙网站建设开发的来说我们尽量少用视图,因为它的效率低。对视图操作比直接对表操作慢,可以用stored   procedure来代替她。特别的是不要用视图嵌套,嵌套视图增加了寻找原始资料的难度。我们看视图的本质:它是存放在服务器上的被优化好了

的已经产生了查询规划的SQL。对单个表检索数据时,不要使用指向多个表的视图,直接从表检索或者仅仅包含这个表的视图上读,否则增加了不必要的开销,查询受到干扰.为了加快视图的查询,MsSQL增加了视图索引的功能。 

15、没有必要时不要用DISTINCT和ORDER   BY,这些动作可以改在客户端执行。它们增加了额外的开销。这同UNION   和UNION   ALL一样的道理。 

16、在IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断的次数 

17、当用SELECT   INTO时,它会锁住系统表(sysobjects,sysindexes等等),阻塞其他的连接的存取。创建临时表时用显示申明语句,而不是 select   INTO.   drop   table   t_lxh   begin   tran   select   *  

into   t_lxh   from   chineseresume   where   name   =   ’XYZ’   --commit   在另一个连接中SELECT   *   from   sysobjects可以看到   SELECT   INTO   会锁住系统表,Create   table   也会锁系统表(不管是临时表还是系统表)。所以千万不要在事物内使用它!!!这样的话如果是经常要用的临时表请使用实表,或者临时表变量。 
18、一次更新多条记录比分多次更新每次一条快,就是说批处理好 

19SELECT   COUNT(*)的效率教低,尽量变通他的写法,而EXISTS快.同时请注意区别:   select   count(Field   of   null)   from   Tableselect   count(Field   of   NOT   null)   from   Table   的返回值是不同的。 

20、当服务器的内存够多时,配制线程数量   =   最大连接数+5,这样能发挥最大的效率;否则使用   配制线程数量 <最大连接数启用SQL   SERVER的线程池来解决,如果还是数量   =   最大连接数+5,严重的损害服务器的性能。 

我觉得主要应该从5个方面进行调整: 
1.去掉不必要的大型表的全表扫描 
2.缓存小型表的全表扫描 
3.检验优化索引的使用 
4.检验优化的连接技术 
5.尽可能减少执行计划的Cost

现在简单的举几个例子

Where子句中有“!=”将不使用索引 
select account_name from test where amount != 0  (不使用) 
select account_name from test where amount > 0  (使用)

Where条件中对字段增加处理函数将不使用该列的索引 
select * from emp where to_char(hire_date,’yyyymmdd’)=20080411’ (不使用) 
select * from emp where hire_date = to_char(’20080411’,’yyyymmdd’) (使用)

避免在索引列上使用IS NULL和 IS NOT NULL 
select * from emp where dept_code is not null  (不使用) 
select * from emp where dept_code > 0  (使用)

通配符% 的使用 
select * from emp where name like%A’  (不使用索引) 
select * from emp where name like ’A%’  (使用索引)

最高效的删除重复记录方法 ( 因为使用了ROWID)例子: 
DELETE  FROM  EMP E  WHERE  E.ROWID > (SELECT MIN(X.ROWID) 
FROM  EMP X  WHERE  X.EMP_NO = E.EMP_NO);

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

在java代码中用到preparedStatement的時候尽量少用连接符“+”连接字符串!

1. SELECT子句中避免使用 ‘*’
    当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 ‘*’ 是一个方便的方法.不幸的是,这是一个非常低效的方法. 实际上,ORACLE在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询

数据字典完成的, 这意味着将耗费更多的时间.

2.选择最有效率的表名顺序(只在基于规则的优化器中有效)
     ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理. 在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.当ORACLE

处理多个表时, 会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记

录进行合并.
例如: 表 TAB1 16,384 条记录
      表 TAB2 1      条记录
     选择TAB2作为基础表 (最好的方法)
     select count(*) from tab1,tab2   执行时间0.96秒
     选择TAB2作为基础表 (不佳的方法)
     select count(*) from tab2,tab1   执行时间26.09秒
如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.
例如:   EMP表描述了LOCATION表和CATEGORY表的交集.
SELECT * 
FROM LOCATION L , 
      CATEGORY C,
      EMP E 
WHERE E.EMP_NO BETWEEN 1000 AND 2000
AND E.CAT_NO = C.CAT_NO
AND E.LOCN = L.LOCN
将比下列SQL更有效率
SELECT * 
FROM EMP E ,
LOCATION L , 
      CATEGORY C
WHERE E.CAT_NO = C.CAT_NO
AND E.LOCN = L.LOCN
AND E.EMP_NO BETWEEN 1000 AND 2000

3. WHERE子句中的连接顺序.
   ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
例如:
(低效,执行时间156.3秒)
SELECTFROM EMP E
WHERE SAL > 50000
AND    JOB = ‘MANAGER’
AND    25 < (SELECT COUNT(*) FROM EMP
             WHERE MGR=E.EMPNO);
(高效,执行时间10.6秒)
SELECTFROM EMP E
WHERE 25 < (SELECT COUNT(*) FROM EMP
             WHERE MGR=E.EMPNO)
AND    SAL > 50000
AND    JOB = ‘MANAGER’;

4. 减少访问数据库的次数
   当执行每条SQL语句时, ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等等. 由此可见, 减少访问数据库的次数 , 就能实际上减少ORACLE的工作量.
例如,
    以下有三种方法可以检索出雇员号等于0342或0291的职员.
方法1 (最低效)
    SELECT EMP_NAME , SALARY , GRADE
    FROM EMP 
    WHERE EMP_NO = 342;
     SELECT EMP_NAME , SALARY , GRADE
    FROM EMP 
    WHERE EMP_NO = 291;
方法2 (次低效)
       DECLARE 
        CURSOR C1 (E_NO NUMBER) IS 
        SELECT EMP_NAME,SALARY,GRADE
        FROM EMP 
        WHERE EMP_NO = E_NO;
    BEGIN 
        OPEN C1(342);
        FETCH C1 INTO …,..,.. ;
                OPEN C1(291);
       FETCH C1 INTO …,..,.. ;
         CLOSE C1;
      END;
方法3 (高效)
    SELECT A.EMP_NAME , A.SALARY , A.GRADE,
            B.EMP_NAME , B.SALARY , B.GRADE
    FROM EMP A,EMP B
    WHERE A.EMP_NO = 342
    AND   B.EMP_NO = 291;
注意:
在SQL*Plus , SQL*Forms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量 ,建议值为200.

5.使用DECODE函数来减少处理时间
   使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表.
例如:
   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函数高效地得到相同结果
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子句中.

6 使用表的别名(Alias)
   当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误.
(译者注: Column歧义指的是由于SQL中不同的表具有相同的Column名,当SQL语句中出现这个Column时,SQL解析器无法判断这个Column的归属)

7. 用EXISTS替代IN
在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率.
低效:
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’)
(译者按: 相对来说,用NOT EXISTS替换NOT IN 将更显著地提高效率,下一节中将指出)

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

EXISTS.
例如:
SELECTFROM EMP
WHERE DEPT_NO NOT IN (SELECT DEPT_NO 
                         FROM DEPT 
                         WHERE 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
     通常来说 , 采用表连接的方式比EXISTS更有效率
      SELECT ENAME
      FROM EMP E
      WHERE EXISTS (SELECT ‘X’ 
                      FROM DEPT
                      WHERE DEPT_NO = E.DEPT_NO
                      AND DEPT_CAT = ‘A’);
     (更高效)
      SELECT ENAME
      FROM DEPT D,EMP E
      WHERE E.DEPT_NO = D.DEPT_NO
      AND DEPT_CAT = ‘A’ ;
   
10.   用EXISTS替换DISTINCT
当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换
例如:
低效:
    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核心模块将在子查询的条件一旦满足后,立刻返回结果.

11. 用外连接代替NOT EXISTS
低效写法:
select count(t.fid)       
from tableA t       
where not exists       
(select t1.fid       
          from tableB t1       
         where t.fid = t1.fid)       
   and t.fdatastatus = ’unrelease’       
   and t.fdevicetypeid in       
       (select t2.fid from tableC t2 where t2.fcategory = ’保护’);

高效写法:
select count(t.fid)        
from tableA t, tableB t1        
where t.fid = t1.fid(+)        
and t.fdatastatus = ’unrelease’        
and t.fdevicetypeid in        
     (select t2.fid from tableC t2 where t2.fcategory = ’保护’)        
and t1.fid is null;

条件1:tableA表中有181901 ,tableB表中有195条数据;查出满足条件的数据4798
   低效写法用时:2.2874秒
   高效写法用时:0.8814秒

条件2:tableA表中有298870 ,tableB表中有89338条数据;查出满足条件的数据30909
   低效写法用时:半小时执行不出结果(直接费了)
   高效写法用时:6.2964秒

注:通过上面两条SQL语句分析,除法(不包含)用not exists的执行效率在数据量大的情况下也是很低的,我们可以通过左右链接的方法同样能满足这样的需求,且效率上有惊人的变化。转载请注明www.sendawangluo.com

 

posted @ 2014-11-10 21:16  博斯芮网络科技  阅读(257)  评论(0编辑  收藏  举报