sql语句优化-赵文斌
Access软件网QQ交流学习群(群号码198465573),欢迎您的加入!
首页 >技术文章> ADP及SQL SERVER


sql语句优化

发表时间:2014/4/20 15:53:12 评论(1) 浏览(8660)  评论 | 加入收藏 | 复制
   
摘 要:sql语句优化之SQL Server
正 文:

sql语句优化之SQL Server

MS   SQL   Server查询优化办法

查询速度慢的原因很多,常见如下几种  

        1、没有索引或者没有效到索引(这是查询慢最常见的题目,是法度设计的缺点)  
        2、I/O吞吐量小,形成了瓶颈效应。  
        3、没有创建策画列导致查询不优化。  
        4、内存不足  
        5、收集速度慢  
        6、查询出的数据量过大(可以采取多次查询,其他的办法降落数据量)  
        7、锁或者死锁(这也是查询慢最常见的题目,是法度设计的缺点)  
        8、s p_lock,s p_who,活动的用户查看,原因是读写竞争资料。  
        9、返回了不须要的行和列  
        10、查询语句不好,没有优化  

        可以经由过程如下办法来优化查询  

        1、把数据、日记、索引放到不合的I/O设备上,增长读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支撑。数据量(尺寸)越大,进步I/O越首要.  
        2、纵向、横向分别表,削减表的尺寸(s p_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、用s p_configure   ""query   governor   cost   limit""或者SET   QUERY_GOVERNOR_COST_LIMIT来限制查询消费的资料。当评估查询消费的资料超出限制时,办事器主动作废查询,在查询之前就扼杀掉。 SET   LOCKTIME设置锁的时候  
21、用   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,11)   =   ""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以前是最首要的手段。例如病院的住院费策画。  

        26、MIN()   和   MAX()能应用到合适的索引  

        27、数据库有一个原则是代码离数据越近越好,所以优先选择Default,依次为Rules,Triggers,   Constraint(束缚如外健主健CheckUNIQUE……,数据类型的最大长度等等都是束缚),Procedure.如许不仅保护工作小,编写法度质量高,并且履行的速度快。  

        28、若是要插入大的二进制值到Image列,应用存储过程,切切不要用内嵌Insert来插入(不知JAVA是否)。因为如许应用法度起首将二进制值转换成字符串(尺寸是它的两倍),办事器受到字符后又将他转换成二进制值.存储过程就没有这些动作:   办法:Create   procedure   p_   as      into   table(Fimage)   values   (@image),   在前台调用这个存储过程传入二进制参数,如许处理惩罚速度明显改良。  

            29、Between在某些时辰比IN速度更快,Between可以或许更快地按照索引找到局限。用查询优化器可见到差别。      *      chineseresume   where   title  in   (""男"",""女"")   Select   *      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等等),梗阻其他的连接的存取。创建姑且表时用显示申明语句,而不是    INTO.  drop   table   t_lxh   begin   tran      *   into   t_lxh      chineseresume   where   name   =   ""XYZ""   --commit   
在另一个连接中Select   *      sysobjects可以看到   Select   INTO   
会锁住体系表,Create   table   
也会锁体系表(不管是姑且表还是体系表)。所以切切不要在事物内应用它!!!如许的话若是是经常要用的姑且表请应用实表,或者姑且表变量。  

        37、一般在GROUP   BY   个HAVING字句之前就能剔除多余的行,所以尽量不要用它们来做剔除行的工作。他们的履行次序应当如下最优:   的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、不要在一句话里再三的应用雷同的函数,浪费资料,将成果放在变量里再调用更快  

        43、Select   COUNT(*)的效力教低,尽量变通他的写法,而EXISTS快.同时请重视差别:      count(Field   of   null)      Table   和      count(Field  of   NOT   null)      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、解析   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 EXECUTI , DISK_READS, BUFFER_GETS,  
ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,  
ROUND(DISK_READS/EXECUTI,2) Reads_per_run,  
SQL_TEXT  
FROM V¥SQLAREA  
Where EXECUTI>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_TRANSACTI  
Where TRAN_DATE = ""31-DEC-95""  
UNION  
Select ACCT_NUM, BALANCE_AMT  
FROM DEBIT_TRANSACTI  
Where TRAN_DATE = ""31-DEC-95""  
高效:  
Select ACCT_NUM, BALANCE_AMT  
FROM DEBIT_TRANSACTI  
Where TRAN_DATE = ""31-DEC-95""  
UNION ALL  
Select ACCT_NUM, BALANCE_AMT  
FROM DEBIT_TRANSACTI  
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语句


 


1、 起首要搞熟悉打听什么叫履行规划?


履行规划是数据库按照SQL语句和相干表的统计信息作出的一个查询规划,这个规划是由查询优化器主动解析产生的,比如一条SQL语句若是用来从一个 

10万笔记录的表中查1笔记录,那查询优化器会选择“索引查找”体式格式,若是该表进行了归档,当前只剩下5000笔记录了,那查询优化器就会改变规划,采取 
“全表扫描”体式格式。


可见,履行规划并不是固定的,它是“个性化的”。产生一个正确的“履行规划”有两点很首要:


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


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


2、 同一SQL语句的写法


对于以下两句SQL语句,法度员认为是雷同的,数据库查询优化器认为是不合的。 



* dual 

*From dual










其实就是大小写不合,查询解析器就认为是两句不合的SQL语句,必须进行两次解析。生成2个履行规划。所以作为法度员,应当包管雷同的查询语句在任何处所都一致,多一个空格都不可!


3、 不要把SQL语句写得太错杂


我经常看到,从数据库中捕获到的一条SQL语句打印出来有2张A4纸这么长。一般来说这么错杂的语句凡是都是有题目的。我拿着这2页长的SQL语句去就教原作者,成果他说时候太长,他一时也看不懂了。可想而知,连原作者都有可能看糊涂的SQL语句,数据库也一样会看糊涂。


一般,将一个Select语句的成果作为子集,然后从该子集中再进行查询,这种一层嵌套语句还是斗劲常见的,然则按照经验,跨越3层嵌套,查询优化 
器就很轻易给失足误的履行规划。因为它被绕晕了。像这种类似人工智能的器材,毕竟比人的辨别力要差些,若是人都看晕了,我可以包管数据库也会晕的。


别的,履行规划是可以被重用的,越简单的SQL语句被重用的可能性越高。而错杂的SQL语句只要有一个字符产生变更就必须从头解析,然后再把这一大堆垃圾塞在内存里。可想而知,数据库的效力会多么低下。


4、 应用“姑且表”暂存中心成果


简化SQL语句的首要办法就是采取姑且表暂存中心成果,然则,姑且表的益处远远不止这些,将姑且成果暂存在姑且表,后面的查询就在tempdb中了,这可以避免法度中多次扫描主表,也大大削减了法度履行中“共享锁”梗阻“更新锁”,削减了梗阻,进步了并发机能。


5、 OLTP体系SQL语句必须采取绑定变量 



* orderheader where changetime >""2010-10-20 00:00:01"" 
* orderheader where changetime >""2010-09-22 00:00:01""






 


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



* orderheader where changetime >@chgtime





@chgtime变量可以传入任何值,如许多量的类似查询可以重用该履行规划了,这可以大大降落数据库解析SQL语句的肩负。一次解析,多次重用,是进步数据库效力的原则。


6、 绑定变量窥测


事物都存在两面性,绑定变量对大多半OLTP处理惩罚是实用的,然则也有例外。比如在where前提中的字段是“倾斜字段”的时辰。


“倾斜字段”指该列中的绝大多半的值都是雷同的,比如一张人口查询拜访表,此中“民族”这列,90%以上都是汉族。那么若是一个SQL语句要查询30岁的汉族人口有几许,那“民族”这列必定要被放在where前提中。这个时辰若是采取绑定变量@nation会存在很大题目。


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


7、 只在须要的景象下才应用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。


8、 一些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 。


9、 凑集索引没有建在表的次序字段上,该表轻易产生页割据


比如订单表,有订单编号orderid,也有客户编号contactid,那么凑集索引应当加在哪个字段上呢?对于该表,订单编号是次序添加的,如 
果在orderid上加凑集索引,新增的行都是添加在末尾,如许不轻易经常产生页割据。然而,因为大多半查询都是按照客户编号来查的,是以,将凑集索引加 
在contactid上才有意义。而contactid对于订单表而言,并非次序字段。


比如“张三”的“contactid”是001,那么“张三”的订单信息必须都放在这张表的第一个数据页上,若是今天“张三”新下了一个订单,那该订单信息不克不及放在表的最后一页,而是第一页!若是第一页放满了呢?很抱愧,该表所稀有据都要往后移动为这笔记录腾处所。


SQL Server的索引和Oracle的索引是不合的,SQL 
Server的凑集索引实际上是对表遵守凑集索引字段的次序进行了排序,相当于oracle的索引组织表。SQL 
Server的凑集索引就是表本身的一种组织情势,所以它的效力是很是高的。也正因为此,插入一笔记录,它的地位不是随便放的,而是要遵守次序放在该放的 
数据页,若是那个数据页没有空间了,就引起了页割据。所以很显然,凑集索引没有建在表的次序字段上,该表轻易产生页割据。


曾经碰着过一个景象,一位哥们的某张表重建索引后,插入的效力大幅降落了。估计景象可能是如许的。该表的凑集索引可能没有建在表的次序字段上,该表 
经常被归档,所以该表的数据是以一种稀少状况存在的。比如张三下过20张订单,而比来3个月的订单只有5张,归档策略是保存3个月数据,那么张三畴昔的 
15张订单已经被归档,留下15个空位,可以在产生时从头被哄骗。在这种景象下因为有空位可以哄骗,就不会产生页割据。然则查询机能会斗劲 
低,因为查询时必须扫描那些没稀有据的空位。


重建凑集索引后景象改变了,因为重建凑集索引就是把表中的数据从头分列一遍,本来的空位没有了,而页的填充率又很高,插入数据经常要产生页割据,所以机能大幅降落。


对于凑集索引没有建在次序字段上的表,是否要授与斗劲低的页填充率?是否要避免重建凑集索引?是一个值得推敲的题目!


10、加nolock后查询经常产生页割据的表,轻易产生跳读或反复读


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


上方提到的哥们,在加了nolock后一些操纵呈现报错,估计有可能因为nolock查询产生了反复读,2条雷同的记录去插入此外表,当然会产生主键冲突。


11、应用like进行模糊查询时应重视


有的时辰会须要进行一些模糊查询比如



* contact where username like ‘%yue%’





 


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


12、数据类型的隐式转换对查询效力的影响


sql server2000的数据库,我们的法度在提交sql语句的时辰,没有应用强类型提交这个字段的值,由sql server 
2000主动转换数据类型,会导致传入的参数与主键字段类型不一致,这个时辰sql server 
2000可能就会应用全表扫描。Sql2005上没有发明这种题目,然则还是应当重视一下。


13、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,赶紧进级吧。



Access软件网交流QQ群(群号:198465573)
 
 相关文章
给我启发的一篇文章:SQL SERVER优化建议  【爱好  2011/8/5】
Sql优化查询速度50个方法小结\SQL Server速度查询优化...  【风行  2012/8/4】
SQL语句在access和SQL中的区别  【宏鹏  2013/9/4】
存储过程编写经验和优化措施  【周芳(转)  2013/9/17】
经典SQL语句大全2  【杜威  2013/12/1】
常用SQL语句汇总整理  【杜威  2013/12/1】
常见问答
技术分类
相关资源
文章搜索
关于作者

赵文斌

文章分类

文章存档

友情链接