还剩11页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
SQL的书写规范及高级进阶优化技巧由于工作需要,最近做了很多BI取数的工作,需要用到一些比较高级的SQL技巧,总结了一下工作中用到的一些比较骚的进阶技巧,特此记录一下,以方便自己查阅,主要目录如下SQL的书写规范SQL的一些进阶使用技巧SQL的优化方法SQL的书写规范在介绍一些技巧之前,有必要强调一下规范,这一点我发现工作中经常被人忽略,其实遵循好的规范可读性会好很多,应该遵循哪些规范呢
1、表名要有意义,且标准SQL中规定表名的第一个字符应该是字母
2、注释,有单行注释和多行注释,如下-单行注释-ASomeTabLe中查询col_lSELECTcol_lFROMSomeTable;/*多行注释从SomeTabLe中查询coL1/—SELECTcol_lFROMSomeTable;多行注释很多人不知道,这种写法不仅可以用来添加真正的注释,也可以用来注释代码,非常方便
3、缩进就像写JavaPython等编程语言一样,SQL也应该有缩进,良好的缩进对提升代码的可读性帮助很大,以下分别是好的缩进与坏的缩进示例-好的缩进SELECTcol1col_2col_3COUNT,*FROMtbl_AWHEREcol_l=ANDcol_2=不过更好的方式是使用EXISTS:SELECTitem_noFROMItemsIWHEREEXISTSSELECT*FROMSalesHistorySHWHEREI.item_no=SH.item_no;既用到了索引,又避免了排序对性能的损耗
二、在极值函数中使用索引MAX/MIN使用MAX/MIN都会对进行排序,如果参数字段上没加索引会导致全表扫描,如果建有索引,则只需要扫描索引即可,对比如下-这样写需要扫描全表SELECTMAXitemFROMItems;-这样写能用到索引SELECTMAXitem_noFROMItems;注意极值函数参数推荐为索引列中并不是不需要排序,而是优化了排序前的查找速度毕竟索引本身就是有序排列的
三、能写在WHERE子句里的条件不要写在HAVING子句里下列SQL语句返回的结果是一样的-聚合后使用HAVING子句过滤SELECTsale_dateSUMquantityFROMSalesHistoryGROUPBYsale_dateHAVINGsale_date=12007-10-01;-聚合前使用WHERE子句过滤SELECTsale_dateSUMquantityFROMSalesHistoryWHEREsale_date=2007-10-01GROUPBYsale_date;使用第二条语句效率更高,原因主要有两点.使用GROUPBY子句进行聚合时会进行排序,如果事先通过WHERE子句能筛选出一部分行,能减轻排序的负担.在WHERE子句中可以使用索引,而HAVING子句是针对聚合后生成的视频进行筛选的,但很多时候聚合后生成的视图并没有保留原表的索引结构
四、在GROUPBY子句和ORDERBY子句中使用索引GROUPBY子句和ORDERBY子句一般都会进行排序,以对行进行排列和替换,不过如果指定带有索引的列作为这两者的参数列,由于用到了索引可以实现高速查询,由于索引是有序的,排序本身都会被省略掉
五、使用索引时,条件表达式的左侧应该是原始字段假设我们在col列上建立了索引,则下面这些SQL语句无法用到索引SELECT*FROMSomeTableWHEREcol*
1.1100;SELECT*FROMSomeTableWHERESUBSTRcol11=a;以上第一个SQL在索引列上进行了运算第二个SQL对索引列使用了函数均无法用到索引,正确方式是把列单独放在左侧如下SELECT*FROMSomeTableIaIHEREcol_l100/
1.1;当然如果需要对此列使用函数,则无法避免在左侧运算,可以考虑使用函数索引,不过一般不推荐随意这么做
六、尽量避免使用否定形式如下的几种否定形式不能用到索引:1=NOTIN所以以下了SQL语句会导致全表扫描SELECT*FROMSomeTableWHEREcol_l100;可以改成以下形式SELECT*FROMSomeTableWHEREcol_l100orcol_l100;
七、进行默认的类型转换假设col是char类型,则推荐使用以下第二,三条SQL的写法,不推荐第一条SQL的写法虽然第一条SQL会默认把10转成工0二但这种默认类型转换不仅会增加额外的性能开销,还会导致索引不可用,所以建议使用的时候进行类型转换
八、减少中间表在SQL中,子查询的结果会产生一张新表,不过如果不加限制大量使用中间表的话,会带来两个问题,一是展示数据需要消耗内存资源,二是原始表中的索引不容易用到,所以尽量减少中间表也可以提升性能
九、灵活使用HAVING子句这一点与上面第八条相呼应,对聚合结果指定筛选条件时,使用HAVING是基本的原则,可能一些工程师会倾向于使用下面这样的写法SELECT*FROMSELECTsale_dateMAXquantityASmax_qtyFROMSalesHistoryGROUPBYsale_dateTMPWHEREmax_qty=10;虽然上面这样的写法能达到目的,但会生成TMP这张临时表,所以应该使用下面这样的写法SELECTsale_dateMAXquantityFROMSalesHistoryGROUPBYsale_dateHAVINGMAXquantity=10;HAVING子句和聚合操作是同时执行的,所以比起生成中间表后再执行HAVING子句,效率会更高,代码也更简洁
10、需要对多个字段使用IN谓词时,将它们汇总到一处一个表的多个字段可能都使用了IN谓词,如下SELECTidstatecityFROMAddresseslAlIaIHEREstateINSELECTFROMWHEREANDcityINSELECTFROM这段代码用到了两个子查询,也就产生了两个中间表,可以像下面这样写这样子查询不用考虑关联性,没有中间表产生,而且只执行一次即可总结本文一开始花了挺大的篇幅来讲解SQL的规范,请大家务必重视这部分内部,良好的规范有利于团队协作,对于代码的阅读也比较友好之后介绍了一些SQL的比较高级的用法,巧用这些技巧确实能达到事半功倍的效果,由于本文篇幅有限只是介绍了一部分,下篇我们会再介绍一些其他的技巧,敬请期待哦GROUPBYcol1col_2col3-坏的示例SELECTcoll_lcol_2col_3COUNT*FROMtbl_AWHEREcoll_l=aANDCO11Z2=SELECTMAXCO1_2FROMtbl_BWHEREcol_3=100GROUPBYcol_lcol_2col_
34、空格代码中应该适当留有一些空格,如果一点不留,代码都凑到一起,逻辑单元不明确,阅读的人也会产生额外的压力,以下分别是是好的与坏的示例-好的示例SELECTcol_lFROMtbl_AAtbl_BBWHEREA.col_l=100ORA.col_2INa|bANDA.col3=B.col3;-坏的不例SELECTcol_lFROMtbl_AAtbl_BBWHEREA.col_l=100ORA.col_2INabANDA.col_3=B.col_3;
4、大小写关键字使用大小写,表名列名使用小写,如下SELECTcol_lcol_2col_3COUNT*FROMtbl_AWHEREcol_l=aANDcol_2=SELECTMAXcol_2FROMtbl_BWHEREcol_3=100GROUPBYcol_lcol_2col_3花了这么多时间强调规范,有必要吗,有!好的规范让代码的可读性更好更有利于团队合作,之后的SQL示例都会遵循这些规范SQL的一些进阶使用技巧
一、巧用CASEWHEN进行统计来看看如何巧用CASEWHEN进行定制化统计假设我们有如下的需求,希望根据左边各个市的人口统计每个省的人口统计结果使用CASEWHEN如下SELECTCASEpref_nameWHEN长沙,THENWHEN,衡阳,THENWHEN海口THENWHEN三亚THENELSE,其他,ENDASdSUMpopulationFROMPopTblGROUPBYdistrict;
二、巧用CASEWHEN进行更新现在某公司员人工资信息表如下:Salaries现在公司出台了一个奇葩的规定.对当前工资为1万以上的员工,降薪10%.对当前工资低于1万的员工,加薪20%一些人不假思索可能写出了以下的SQL:--条件1UPDATESalariesSETsalary=salary*
0.9WHEREsalary=10000;--条件2UPDATESalariesSETsalary=salary*
1.2WHEREsalary10000;这么做其实是有问题的,什么问题,对小明来说,他的工资是10500执行第一个SQL后,工资变为10500*
0.9=9450紧接着又执行条件2工资变为了9450*
1.2=11340反而涨薪了!如果用CASEWHEN可以解决此类问题,如下UPDATESalariesSETsalary=CASEWHENsalary=10000THENsalary*
0.9WHENsalary10000THENsalary*
1.2ELSEsalaryEND;
三、巧用HAVING子句一般HAVING是与GROUPBY结合使用的,但其实它是可以独立使用的,假设有如下表,第一列seq叫连续编号,但其实有些编号是缺失的,怎么知道编号是否缺失呢,SeqTbl用HAVING表示如下SELECT存在缺失的编号ASgapFROMSeqTblHAVINGCOUNT*MAXseq;
四、自连接针对相同的表进行的连接被称为“自连接”(selfjoin)这个技巧常常被人们忽视,其实是有挺多妙用的
1、删除重复行删除重复行上图中有三个橘子,需要把这些重复的行给删掉,用如下自连接可以解决:DELETEFROMProductsPlWHEREidSELECTMAXP
2.idFROMProductsP2WHEREPl.name=P
2.nameANDPl.price=P
2.price;
2、排序在db中,我们经常需要按分数,人数,销售额等进行排名,有OracleDB2中可以使用RANK函数进行排名,不过在MySQL中RANK函数未实现,这种情况我们可以使用自连接来实现,如对以下Products表按价格高低进行排名Products使用自连接可以这么写:排序从1开始如果已出现相同位次则跳过之后的位次SELECTPl.namePl・priceSELECTC0UNTP
2.priceFROMProductsP2WHEREP
2.pricePl.price+1ASrank_lFROMProductsPl-ORDERBYrank_l;结果如下:namepricerank此函数作用返回参数中的第一个非空表达式,假设有如下商品,我们重新格式化一样,如果city为null代表商品不在此城市发行,但我们在展示结果的时候不想展示null而想展示N/A可以这么做SELECTCOALESCECcityN/AFROMcustomers;ProductSQL性能优化技巧
一、参数是子查询时,使用EXISTS代替IN如果IN的参数是
(123)这样的值列表时,没啥问题,但如果参数是子查询时,就需要注意了比如,现在有如下两个表Class_A现在我们要查出同时存在于两个表的员工,即田中和铃木,则以下用IN和EXISTS返回的结果是一样,但是用EXISTS的SQL会更快--慢SELECT*FROMClass_AWHEREidIN^SELECTidFROMCLASS_B;--快SELECT*FROMClass_AAIaIHEREEXISTSSELECT*FROMClass_BBWHEREA.id=B.id;为啥使用EXISTS的SQL运行更快呢,有两个原因.可以、用到索引,如果连接列(id)上建立了索引,那么查询Class_B时不用查实际的表,只需查索引就可以了.如果使用EXISTS那么只要查到一行数据满足条件就会终止查询,不用像使用IN时一样扫描全表在这一点上NOTEXISTS也一样另外如果IN后面如果跟着的是子查询,由于SQL会先执行IN后面的子查询,会将子查询的结果保存在一张临时的工作表里(内联视图),然后扫描整个视图,显然扫描整个视图这个工作很多时候是非常耗时的,而用EXISTS不会生成临时表当然了,如果IN的参数是子查询时,也可以用连接来代替,如下--使用连接代替INSELECTA.idA.nameFROMClass_AAINNERJOINClass_BBONA.id=B.id;用到了FidJ列上的索引,而且由于没有子查询,也不会生成临时表
二、避免排序SQL是声明式语言,即对用户来说,只关心它能做什么,不用关心它怎么做这样可能会产生潜在的性能问题排序,会产生排序的代表性运算有下面这些GROUPBY子句ORDERBY子句聚合函数(SUM、COUNT、AVG、MAX、MIN)DISTINCT•集合运算符(UNION、INTERSECT、EXCEPT)•窗口函数(RANK、ROW_NUMBER如果在内存中排序还好,但如果内存不够导致需要在硬盘上排序上的话,性能就会急剧下降,所以我们需要减少不必要的排序怎样做可以减少排序呢
1、使用集合运算符的ALL可选项SQL中有UNIONINTERSECTEXCEPT三个集合运算符,默认情况下,这些运算符会为了避免重复数据而进行排序,对比一下使用UNION运算符加和不加ALL的情况idname4小强注意加ALL是优化性能非常有效的手段,不过前提是不在乎结果是否有重复数据
2、使用EXISTS代表DISTINCT为了排除重复数据,DISTINCT也会对结果进行排序,如果需要对两张表的连接结果进行去重,可以考虑用EXISTS代替DISTINCT这样可以避免排序namesalaryname(商品名称)price(价格)name(商品名称)price(价格)苹果50橘子100香蕉80橘子10050西瓜80柠檬30香蕉50idcity1null2杭州市3北京市。
个人认证
优秀文档
获得点赞 0