整理:sql语句优化之SQL Server

 

MS   SQL   Server查询优化方法 查询速度慢的缘由不少,常见以下几种javascript

         1、没有索引或者没有用到索引(这是查询慢最多见的问题,是程序设计的缺陷)java

         2、I/O吞吐量小,造成了瓶颈效应。程序员

          3、没有建立计算列致使查询不优化。sql

          4、内存不足数据库

          5、网络速度慢编程

          6、查询出的数据量过大(能够采用屡次查询,其余的方法下降数据量)服务器

          7、锁或者死锁(这也是查询慢最多见的问题,是程序设计的缺陷)网络

          8、sp_lock,sp_who,活动的用户查看,缘由是读写竞争资源。并发

          9、返回了没必要要的行和列oracle

          10、查询语句很差,没有优化 
        能够经过以下方法来优化查询 
       
1、把数据、日志、索引放到不一样的I/O设备上,增长读取速度,之前能够将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提升I/O越重要.

          2、纵向、横向分割表,减小表的尺寸(sp_spaceuse)

          3、升级硬件

          4、根据查询条件,创建索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽可能小,使用字节数小的列建索引好(参照索引的建立),不要对有限的几个值的字段建单一索引如性别字段

          5、提升网速;

          6、扩大服务器的内存,Windows   2000和SQL   server   2000能支持4-8G的内存。配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行   Microsoft   SQL   Server?   2000   时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的   1.5   倍。若是另外安装了全文检索功能,并打算运行   Microsoft   搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的   3   倍。将   SQL   Server   max   server   memory   服务器配置选项配置为物理内存的   1.5   倍(虚拟内存大小设置的一半)。

          7、增长服务器CPU个数;可是必须明白并行处理串行处理更须要资源例如内存。使用并行仍是串行程是MsSQL自动评估选择的。单个任务分解成多个任务,就能够在处理器上运行。例如耽搁查询的排序、链接、扫描和GROUP   BY字句同时执行,SQL   SERVER根据系统的负载状况决定最优的并行等级,复杂的须要消耗大量的CPU的查询最适合并行处理。可是更新操做UPDATE,INSERT, DELETE还不能并行处理。

          8、若是是使用like进行查询的话,简单的使用index是不行的,可是全文索引,耗空间。   like   'a%'   使用索引   like   '%a'   不使用索引用   like   '%a%'   查询时,查询耗时和字段值总长度成正比,因此不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。

          9、DB   Server   和APPLication   Server   分离;OLTP和OLAP分离

          10、分布式分区视图可用于实现数据库服务器联合体。联合体是一组分开管理的服务器,但它们相互协做分担系统的处理负荷。这种经过分区数据造成数据库服务器联合体的机制可以扩大一组服务器,以支持大型的多层   Web   站点的处理须要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件'分区视图') 
          a、在实现分区视图以前,必须先水平分区表

          b、在建立成员表后,在每一个成员服务器上定义一个分布式分区视图,而且每一个视图具备相同的名称。这样,引用分布式分区视图名的查询能够在任何一个成员服务器上运行。系统操做如同每一个成员服务器上都有一个原始表的复本同样,但其实每一个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。 
     
11、重建索引   DBCC   REINDEX   ,DBCC   INDEXDEFRAG,收缩数据和日志   DBCC   SHRINKDB,DBCC   SHRINKFILE.   设置自动收缩日志.对于大的数据库不要设置数据库自动增加,它会下降服务器的性能。   在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:

  
         
1、   查询语句的词法、语法检查

          2、   将语句提交给DBMS的查询优化器

          3、   优化器作代数优化和存取路径的优化

          4、   由预编译模块生成查询规划

          5、   而后在合适的时间提交给系统处理执行

          6、   最后将执行结果返回给用户其次,看一下SQL   SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。 


       
12、Commit和rollback的区别

   Rollback:回滚全部的事物。

   Commit:提交当前的事物.

   没有必要在动态SQL里写事物,若是要写请写在外面如:   begin   tran   exec(@s)   commit   trans   或者将动态SQL   写成函数或者存储过程。 
       
13、在查询Select语句中用Where字句限制返回的行数,避免表扫描,若是返回没必要要的数据,浪费了服务器的I/O资源,加剧了网络的负担下降性能。若是表很大,在表扫描的期间将表锁住,禁止其余的联接访问表,后果严重。 
       
14、SQL的注释申明对执行没有任何影响 
       
15、尽量不使用光标,它占用大量的资源。若是须要row-by-row地执行,尽可能采用非光标技术,如:在客户端循环,用临时表,Table变量,用子查询,用Case语句等等。游标能够按照它所支持的提取选项进行分类:   只进   必须按照从第一行到最后一行的顺序提取行。FETCH   NEXT   是惟一容许的提取操做,也是默认方式。可滚动性   能够在游标中任何地方随机提取任意行。游标的技术在SQL2000下变得功能很强大,他的目的是支持循环。 
        有四个并发选项 
READ_ONLY:不容许经过游标定位更新(
Update),且在组成结果集的行中没有锁。

 
        OPTIMISTIC  
WITH   valueS:乐观并发控制是事务控制理论的一个标准部分。乐观并发控制用于这样的情形,即在打开游标及更新行的间隔中,只有很小的机会让第二个用户更新某一行。当某个游标以此选项打开时,没有锁控制其中的行,这将有助于最大化其处理能力。若是用户试图修改某一行,则此行的当前值会与最后一次提取此行时获取的值进行比较。若是任何值发生改变,则服务器就会知道其余人已更新了此行,并会返回一个错误。若是值是同样的,服务器就执行修改。   选择这个并发选项OPTIMISTIC   WITH   ROW   VERSIONING:此乐观并发控制选项基于行版本控制。使用行版本控制,其中的表必须具备某种版本标识符,服务器可用它来肯定该行在读入游标后是否有所更改。

          在   SQL   Server   中,这个性能由   timestamp   数据类型提供,它是一个二进制数字,表示数据库中更改的相对顺序。每一个数据库都有一个全局当前时间戳值:@@DBTS。每次以任何方式更改带有   timestamp   列的行时,SQL   Server   先在时间戳列中存储当前的   @@DBTS   值,而后增长   @@DBTS   的值。若是某   个表具备   timestamp   列,则时间戳会被记到行级。服务器就能够比较某行的当前时间戳值和上次提取时所存储的时间戳值,从而肯定该行是否已更新。服务器没必要比较全部列的值,只需比较   timestamp   列便可。若是应用程序对没有   timestamp   列的表要求基于行版本控制的乐观并发,则游标默认为基于数值的乐观并发控制。

          SCROLL   LOCKS   这个选项实现悲观并发控制。在悲观并发控制中,在把数据库的行读入游标结果集时,应用程序将试图锁定数据库行。在使用服务器游标时,将行读入游标时会在其上放置一个更新锁。若是在事务内打开游标,则该事务更新锁将一直保持到事务被提交或回滚;当提取下一行时,将除去游标锁。若是在事务外打开游标,则提取下一行时,锁就被丢弃。所以,每当用户须要彻底的悲观并发控制时,游标都应在事务内打开。更新锁将阻止任何其它任务获取更新锁或排它锁,从而阻止其它任务更新该行。

          然而,更新锁并不阻止共享锁,因此它不会阻止其它任务读取行,除非第二个任务也在要求带更新锁的读取。滚动锁根据在游标定义的   SELECT   语句中指定的锁提示,这些游标并发选项能够生成滚动锁。滚动锁在提取时在每行上获取,并保持到下次提取或者游标关闭,以先发生者为准。下次提取时,服务器为新提取中的行获取滚动锁,并释放上次提取中行的滚动锁。滚动锁独立于事务锁,并能够保持到一个提交或回滚操做以后。若是提交时关闭游标的选项为关,则   COMMIT   语句并不关闭任何打开的游标,并且滚动锁被保留到提交以后,以维护对所提取数据的隔离。所获取滚动锁的类型取决于游标并发选项和游标   SELECT   语句中的锁提示。          锁提示   只读   乐观数值   乐观行版本控制   锁定无提示   未锁定   未锁定   未锁定   更新   NOLOCK   未锁定   未锁定   未锁定   未锁定   HOLDLOCK   共享   共享   共享   更新   UPDLOCK   错误   更新   更新   更新   TABLOCKX   错误   未锁定   未锁定   更新其它   未锁定   未锁定   未锁定   更新   *指定   NOLOCK   提示将使指定了该提示的表在游标内是只读的。 
       
16、用Profiler来跟踪查询,获得查询所需的时间,找出SQL的问题所在;用索引优化器优化索引 
       
17、注意UNion和UNion   all   的区别。UNION   all好 
       
18、注意使用DISTINCT,在没有必要时不要用,它同UNION同样会使查询变慢。重复的记录在查询里是没有问题的 
       
19、查询时不要返回不须要的行、列 
       
20、用sp_configure   'query   governor   cost   limit'或者SET   QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源超出限制时,服务器自动取消查询,在查询以前就扼杀掉。 SET   LOCKTIME设置锁的时间  21、用select   top   100   /   10   Percent   来限制用户返回的行数或者SET   ROWCOUNT来限制操做的行 
       
22、在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   EXISTS   ,IN   ,   LEFT   OUTER   JOIN   来替代,特别是左链接,而Exists比IN更快,最慢的是NOT操做.若是列的值含有空,之前它的索引不起做用,如今2000的优化器可以处理了。相同的是IS   NULL,“NOT",   "NOT   EXISTS",   "NOT   IN"能优化她,而” <> ”等仍是不能优化,用不到索引。 
       
23、使用Query   Analyzer,查看SQL语句的查询计划和评估分析是不是优化的SQL。通常的20%的代码占据了80%的资源,咱们优化的重点是这些慢的地方。 
       
24、若是使用了IN或者OR等时发现查询没有走索引,使用显示申明指定索引:   SELECT   *   FROM   PersonMember   (INDEX   =   IX_Title)   WHERE   processid   IN   (‘男’,‘女’) 
         
25、将须要查询的结果预先计算好放在表中,查询的时候再SELECT。这在SQL7.0之前是最重要的手段。例如医院的住院费计算。 
       
26MIN()   和   MAX()能使用到合适的索引 
       
27、数据库有一个原则是代码离数据越近越好,因此优先选择Default,依次为Rules,Triggers,   Constraint(约束如外健主健CheckUNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不只维护工做小,编写程序质量高,而且执行的速度快。 
       
28、若是要插入大的二进制值到Image列,使用存储过程,千万不要用内嵌INsert来插入(不知JAVA是否)。由于这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储过程就没有这些动做:   方法:Create   procedure   p_insert   as   insert   into   table(Fimage)   values   (@image),   在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。 
           
29、Between在某些时候比IN速度更快,Between可以更快地根据索引找到范围。用查询优化器可见到差异。   select   *   from   chineseresume   where   title   in   ('','')   Select   *   from   chineseresume   where   between   ''   and   ''   是同样的。因为in会在比较屡次,因此有时会慢些。 
         
30、在必要是对全局或者局部临时表建立索引,有时可以提升速度,但不是必定会这样,由于索引也耗费大量的资源。他的建立同是实际表同样。 
       
31、不要建没有做用的事物例如产生报表时,浪费资源。只有在必要使用事物时使用它。                 32、用OR的字句能够分解成多个查询,而且经过UNION   链接多个查询。他们的速度只同是否使用索引有关,若是查询须要用到联合索引,用UNION   all执行的效率更高.多个OR的字句没有用到索引,改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。 
       
33、尽可能少用视图,它的效率低。对视图操做比直接对表操做慢,能够用stored   procedure来代替她。特别的是不要用视图嵌套,嵌套视图增长了寻找原始资料的难度。咱们看视图的本质:它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时,不要使用指向多个表的视图,直接从表检索或者仅仅包含这个表的视图上读,不然增长了没必要要的开销,查询受到干扰.为了加快视图的查询,MsSQL增长了视图索引的功能。 
       
34、没有必要时不要用DISTINCT和ORDER   BY,这些动做能够改在客户端执行。它们增长了额外的开销。这同UNION   和UNION   ALL同样的道理。   SELECT   top   20   ad.companyname,comid,position,ad.referenceid,worklocation,   convert(varchar(10),ad.postDate,120)   as   postDate1,workyear,degreedescription   FROM   jobcn_query.dbo.COMPANYAD_query   ad   where   referenceID   in('JCNAD00329667','JCNAD132168','JCNAD00337748','JCNAD00338345','JCNAD00333138','JCNAD00303570',   'JCNAD00303569','JCNAD00303568','JCNAD00306698','JCNAD00231935','JCNAD00231933','JCNAD00254567',   'JCNAD00254585','JCNAD00254608','JCNAD00254607','JCNAD00258524','JCNAD00332133','JCNAD00268618',   'JCNAD00279196','JCNAD00268613')   order   by   postdate   desc 
       
35、在IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减小判断的次数 
       
36、当用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   也会锁系统表(无论是临时表仍是系统表)。因此千万不要在事物内使用它!!!这样的话若是是常常要用的临时表请使用实表,或者临时表变量。          37、通常在GROUP   BY   个HAVING字句以前就能剔除多余的行,因此尽可能不要用它们来作剔除行的工做。他们的执行顺序应该以下最优:select   的Where字句选择全部合适的行,Group   By用来分组个统计行,Having字句用来剔除多余的分组。这样Group   By   个Having的开销小,查询快.对于大的数据行进行分组和Having十分消耗资源。若是Group   BY的目的不包括计算,只是分组,那么用Distinct更快 
       
38、一次更新多条记录比分屡次更新每次一条快,就是说批处理好 
       
39、少用临时表,尽可能用结果集和Table类性的变量来代替它,Table   类型的变量比临时表好 
       
40、在SQL2000下,计算字段是能够索引的,须要知足的条件以下: 
          a、计算字段的表达是肯定的            b、不能用在TEXT,
Ntext,Image数据类型          c、必须配制以下选项   ANSI_NULLS   =   ON,   ANSI_PADDINGS   =   ON,   ……. 
       
41、尽可能将数据的处理工做放在服务器上,减小网络的开销,如使用存储过程。存储过程是编译好、优化过、而且被组织到一个执行规划里、且存储在数据库中的 SQL语句,是控制流语言的集合,速度固然快。反复执行的动态SQL,可使用临时存储过程,该过程(临时表)被放在Tempdb中。之前因为SQL   SERVER对复杂的数学计算不支持,因此不得不将这个工做放在其余的层上而增长网络的开销。SQL2000支持UDFs,如今支持复杂的数学计算,函数的返回值不要太大,这样的开销很大。用户自定义函数象光标同样执行的消耗大量的资源,若是返回大的结果采用存储过程 
       
42、不要在一句话里再三的使用相同的函数,浪费资源,将结果放在变量里再调用更快 
       
43SELECT   COUNT(*)的效率教低,尽可能变通他的写法,而EXISTS快.同时请注意区别:   select   count(Field   of   null)   from   Table   和   select   count(Field   of   NOT   null)   from   Table   的返回值是不一样的。 
       
44、当服务器的内存够多时,配制线程数量   =   最大链接数+5,这样能发挥最大的效率;不然使用   配制线程数量 <最大链接数启用SQL   SERVER的线程池来解决,若是仍是数量   =   最大链接数+5,严重的损害服务器的性能。 
       
45、按照必定的次序来访问你的表。若是你先锁住表A,再锁住表B,那么在全部的存储过程当中都要按照这个顺序来锁定它们。若是你(不经意的)某个存储过程当中先锁定表B,再锁定表A,这可能就会致使一个死锁。若是锁定顺序没有被预先详细的设计好,死锁很难被发现 
       
46、经过SQL   Server   Performance   Monitor监视相应硬件的负载   Memory:   Page   Faults   /   sec计数器若是该值偶尔走高,代表当时有线程竞争内存。若是持续很高,则内存多是瓶颈。   Process: 
       
1%   DPC   Time   指在范例间隔期间处理器用在缓延程序调用(DPC)接收和提供服务的百分比。(DPC   正在运行的为比标准间隔优先权低的间隔)。   因为   DPC   是以特权模式执行的,DPC   时间的百分比为特权时间   百分比的一部分。这些时间单独计算而且不属于间隔计算总数的一部   分。这个总数显示了做为实例时间百分比的平均忙时。

          2%Processor   Time计数器 若是该参数值持续超过95%,代表瓶颈是CPU。能够考虑增长一个处理器或换一个更快的处理器。

          3%   Privileged   Time   指非闲置处理器时间用于特权模式的百分比。(特权模式是为操做系统组件和操纵硬件驱动程序而设计的一种处理模式。它容许直接访问硬件和全部内存。另外一种模式为用户模式,它是一种为应用程序、环境分系统和整数分系统设计的一种有限处理模式。操做系统将应用程序线程转换成特权模式以访问操做系统服务)。   特权时间的   %   包括为间断和   DPC   提供服务的时间。特权时间比率高多是因为失败设备产生的大数量的间隔而引发的。这个计数器将平均忙时做为样本时间的一部分显示。

          4%   User   Time表示耗费CPU的数据库操做,如排序,执行aggregate   functions等。若是该值很高,可考虑增长索引,尽可能使用简单的表联接,水平分割大表格等方法来下降该值。   Physical   Disk:   Curretn   Disk   Queue   Length计数器该值应不超过磁盘数的1.5~2倍。要提升性能,可增长磁盘。   SQLServer:Cache   Hit   Ratio计数器该值越高越好。若是持续低于80%,应考虑增长内存。   注意该参数值是从SQL   Server启动后,就一直累加记数,因此运行通过一段时间后,该值将不能反映系统当前值。

          47、分析select   emp_name   form   employee   where   salary   >   3000   在此语句中若salary是Float类型的,则优化器对其进行优化为Convert(float,3000),由于3000是个整数,咱们应在编程时使用3000.0而不要等运行时让DBMS进行转化。一样字符和整型数据的转换。

 

 ======================================================================================================

咱们要作到不但会写SQL,还要作到写出性能优良的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) 整合简单,无关联的数据库访问: 若是你有几个简单的数据库查询语句,你能够把它们整合到一个查询中(即便它们之间没有关系)

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

(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 TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT TAB_NAME,DB_VER FROM TAB_COLUMNS 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 FROM 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

  这是一条简单易记的规则,可是实际的执行效果还须检验,在ORACLE8i下,二者的执行路径彷佛是相同的.

  低效:  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

=====================================================================================================

 

 

优化SQL查询:如何写出高性能SQL语句

 

一、 首先要搞明白什么叫执行计划?

执行计划是数据库根据SQL语句和相关表的统计信息做出的一个查询方案,这个方案是由查询优化器自动分析产生的,好比一条SQL语句若是用来从一个 10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,若是该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 “全表扫描”方式。

可见,执行计划并非固定的,它是“个性化的”。产生一个正确的“执行计划”有两点很重要:

(1)    SQL语句是否清晰地告诉查询优化器它想干什么?

(2)    查询优化器获得的数据库统计信息是不是最新的、正确的?

二、 统一SQL语句的写法

对于如下两句SQL语句,程序员认为是相同的,数据库查询优化器认为是不一样的。 

复制代码
select * from dual
select * From dual
复制代码

其实就是大小写不一样,查询分析器就认为是两句不一样的SQL语句,必须进行两次解析。生成2个执行计划。因此做为程序员,应该保证相同的查询语句在任何地方都一致,多一个空格都不行!

三、 不要把SQL语句写得太复杂

我常常看到,从数据库中捕捉到的一条SQL语句打印出来有2张A4纸这么长。通常来讲这么复杂的语句一般都是有问题的。我拿着这2页长的SQL语句去请教原做者,结果他说时间太长,他一时也看不懂了。可想而知,连原做者都有可能看糊涂的SQL语句,数据库也同样会看糊涂。

通常,将一个Select语句的结果做为子集,而后从该子集中再进行查询,这种一层嵌套语句仍是比较常见的,可是根据经验,超过3层嵌套,查询优化器就很容易给出错误的执行计划。由于它被绕晕了。像这种相似人工智能的东西,终究比人的分辨力要差些,若是人都看晕了,我能够保证数据库也会晕的。

另外,执行计划是能够被重用的,越简单的SQL语句被重用的可能性越高。而复杂的SQL语句只要有一个字符发生变化就必须从新解析,而后再把这一大堆垃圾塞在内存里。可想而知,数据库的效率会何等低下。

四、 使用“临时表”暂存中间结果

简化SQL语句的重要方法就是采用临时表暂存中间结果,可是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在tempdb中了,这能够避免程序中屡次扫描主表,也大大减小了程序执行中“共享锁”阻塞“更新锁”,减小了阻塞,提升了并发性能。

五、 OLTP系统SQL语句必须采用绑定变量 

复制代码
select * from orderheader where changetime > ' 2010-10-20 00:00:01 ' select * from orderheader where changetime > ' 2010-09-22 00:00:01 '
复制代码

 

 

以上两句语句,查询优化器认为是不一样的SQL语句,须要解析两次。若是采用绑定变量

复制代码
select * from orderheader where changetime > @chgtime
复制代码

@chgtime变量能够传入任何值,这样大量的相似查询能够重用该执行计划了,这能够大大下降数据库解析SQL语句的负担。一次解析,屡次重用,是提升数据库效率的原则。

六、 绑定变量窥测

事物都存在两面性,绑定变量对大多数OLTP处理是适用的,可是也有例外。好比在where条件中的字段是“倾斜字段”的时候。

“倾斜字段”指该列中的绝大多数的值都是相同的,好比一张人口调查表,其中“民族”这列,90%以上都是汉族。那么若是一个SQL语句要查询30岁的汉族人口有多少,那“民族”这列必然要被放在where条件中。这个时候若是采用绑定变量@nation会存在很大问题。

试想若是@nation传入的第一个值是“汉族”,那整个执行计划必然会选择表扫描。而后,第二个值传入的是“布依族”,按理说“布依族”占的比例可能只有万分之一,应该采用索引查找。可是,因为重用了第一次解析的“汉族”的那个执行计划,那么第二次也将采用表扫描方式。这个问题就是著名的“绑定变量窥测”,建议对于“倾斜字段”不要采用绑定变量。

七、 只在必要的状况下才使用begin tran

SQL Server中一句SQL语句默认就是一个事务,在该语句执行完成后也是默认commit的。其实,这就是begin tran的一个最小化的形式,比如在每句语句开头隐含了一个begin tran,结束时隐含了一个commit。

有些状况下,咱们须要显式声明begin tran,好比作“插、删、改”操做须要同时修改几个表,要求要么几个表都修改为功,要么都不成功。begin tran 能够起到这样的做用,它能够把若干SQL语句套在一块儿执行,最后再一块儿commit。好处是保证了数据的一致性,但任何事情都不是天衣无缝的。Begin tran付出的代价是在提交以前,全部SQL语句锁住的资源都不能释放,直到commit掉。

可见,若是Begin tran套住的SQL语句太多,那数据库的性能就糟糕了。在该大事务提交以前,必然会阻塞别的语句,形成block不少。

Begin tran使用的原则是,在保证数据一致性的前提下,begin tran 套住的SQL语句越少越好!有些状况下能够采用触发器同步数据,不必定要用begin tran。

八、 一些SQL查询语句应加上nolock

在SQL语句中加nolock是提升SQL Server并发性能的重要手段,在oracle中并不须要这样作,由于oracle的结构更为合理,有undo表空间保存“数据前影”,该数据若是在修改中还未commit,那么你读到的是它修改以前的副本,该副本放在undo表空间中。这样,oracle的读、写能够作到互不影响,这也是oracle 广受称赞的地方。SQL Server 的读、写是会相互阻塞的,为了提升并发性能,对于一些查询,能够加上nolock,这样读的时候能够容许写,但缺点是可能读到未提交的脏数据。使用 nolock有3条原则。

(1)    查询的结果用于“插、删、改”的不能加nolock !

(2)    查询的表属于频繁发生页分裂的,慎用nolock !

(3)    使用临时表同样能够保存“数据前影”,起到相似oracle的undo表空间的功能,

能采用临时表提升并发性能的,不要用nolock 。

九、 汇集索引没有建在表的顺序字段上,该表容易发生页分裂

好比订单表,有订单编号orderid,也有客户编号contactid,那么汇集索引应该加在哪一个字段上呢?对于该表,订单编号是顺序添加的,若是在orderid上加汇集索引,新增的行都是添加在末尾,这样不容易常常产生页分裂。然而,因为大多数查询都是根据客户编号来查的,所以,将汇集索引加在contactid上才有意义。而contactid对于订单表而言,并不是顺序字段。

好比“张三”的“contactid”是001,那么“张三”的订单信息必须都放在这张表的第一个数据页上,若是今天“张三”新下了一个订单,那该订单信息不能放在表的最后一页,而是第一页!若是第一页放满了呢?很抱歉,该表全部数据都要日后移动为这条记录腾地方。

SQL Server的索引和Oracle的索引是不一样的,SQL Server的汇集索引其实是对表按照汇集索引字段的顺序进行了排序,至关于oracle的索引组织表。SQL Server的汇集索引就是表自己的一种组织形式,因此它的效率是很是高的。也正由于此,插入一条记录,它的位置不是随便放的,而是要按照顺序放在该放的数据页,若是那个数据页没有空间了,就引发了页分裂。因此很显然,汇集索引没有建在表的顺序字段上,该表容易发生页分裂。

曾经碰到过一个状况,一位哥们的某张表重建索引后,插入的效率大幅降低了。估计状况大概是这样的。该表的汇集索引可能没有建在表的顺序字段上,该表常常被归档,因此该表的数据是以一种稀疏状态存在的。好比张三下过20张订单,而最近3个月的订单只有5张,归档策略是保留3个月数据,那么张三过去的 15张订单已经被归档,留下15个空位,能够在insert发生时从新被利用。在这种状况下因为有空位能够利用,就不会发生页分裂。可是查询性能会比较低,由于查询时必须扫描那些没有数据的空位。

重建汇集索引后状况改变了,由于重建汇集索引就是把表中的数据从新排列一遍,原来的空位没有了,而页的填充率又很高,插入数据常常要发生页分裂,因此性能大幅降低。

对于汇集索引没有建在顺序字段上的表,是否要给与比较低的页填充率?是否要避免重建汇集索引?是一个值得考虑的问题!

十、加nolock后查询常常发生页分裂的表,容易产生跳读或重复读

加nolock后能够在“插、删、改”的同时进行查询,可是因为同时发生“插、删、改”,在某些状况下,一旦该数据页满了,那么页分裂不可避免,而此时nolock的查询正在发生,好比在第100页已经读过的记录,可能会由于页分裂而分到第101页,这有可能使得nolock查询在读101页时重复读到该条数据,产生“重复读”。同理,若是在100页上的数据还没被读到就分到99页去了,那nolock查询有可能会漏过该记录,产生“跳读”。

上面提到的哥们,在加了nolock后一些操做出现报错,估计有可能由于nolock查询产生了重复读,2条相同的记录去插入别的表,固然会发生主键冲突。

十一、使用like进行模糊查询时应注意

有的时候会须要进行一些模糊查询好比

复制代码
select * from contact where username like % yue %
复制代码

 

 

关键词%yue%,因为yue前面用到了“%”,所以该查询必然走全表扫描,除非必要,不然不要在关键词前加%,

十二、数据类型的隐式转换对查询效率的影响

sql server2000的数据库,咱们的程序在提交sql语句的时候,没有使用强类型提交这个字段的值,由sql server 2000自动转换数据类型,会致使传入的参数与主键字段类型不一致,这个时候sql server 2000可能就会使用全表扫描。Sql2005上没有发现这种问题,可是仍是应该注意一下。

1三、SQL Server 表链接的三种方式

(1) Merge Join

(2) Nested Loop Join

(3) Hash Join

SQL Server 2000只有一种join方式——Nested Loop Join,若是A结果集较小,那就默认做为外表,A中每条记录都要去B中扫描一遍,实际扫过的行数至关于A结果集行数x B结果集行数。因此若是两个结果集都很大,那Join的结果很糟糕。

SQL Server 2005新增了Merge Join,若是A表和B表的链接字段正好是汇集索引所在字段,那么表的顺序已经排好,只要两边拼上去就好了,这种join的开销至关于A表的结果集行数加上B表的结果集行数,一个是加,一个是乘,可见merge join 的效果要比Nested Loop Join好多了。

若是链接的字段上没有索引,那SQL2000的效率是至关低的,而SQL2005提供了Hash join,至关于临时给A,B表的结果集加上索引,所以SQL2005的效率比SQL2000有很大提升,我认为,这是一个重要的缘由。

总结一下,在表链接时要注意如下几点:

(1)    链接字段尽可能选择汇集索引所在的字段

(2)    仔细考虑where条件,尽可能减少A、B表的结果集

(3)    若是不少join的链接字段都缺乏索引,而你还在用SQL Server 2000,赶忙升级吧。

相关文章
相关标签/搜索