SQL-放慢-查问的九种低劣通常 (慢sql语句)
译者|陈峻
审校|重楼
如您所知,SQL多年来不时是开发和查问数据库的关键言语。在编程通常中,人们逐渐积攒了各种在经常使用环节中的小技巧。上方,让咱们来看看无关如何编写出更高效的SQL查问的9种低劣通常。
1.只检索须要的列
关于那些所谓的数据库开发老司机而言,他们会有一个经常出现的SQL习气:在编写查问代码时,频繁地经常使用SELECT*,一次性性列出一切或者须要的数据列。显然,假设查问一个存储了一百多列的数据表的一切列,您可以构想会出现什么?毕竟在实在的系统运行环境中,这样的数据表不足为奇,而且它们并非总是可以经过从新设计和优化,来合理化其结构。那么,您能否思考过采取便捷点的方法呢?其实,咱们可以只选择列的子集,以防止在查问环节中占用不用要的资源,并提高口头的效率。
当然,在启动查问的原型设计时,经常使用SELECT*是没有太大疑问的,但是一旦进入消费阶段,详细的查问就应该只恳求那些实践将会经常使用到的数据列。
2.经常使用CASE替代UPDATE启动有条件的列降级
在编程环节中,开发人员也会经常经常使用UPDATE...WHERE,来依据数据表中的某一列的值,设置另一列的值。例如,UPDATEUsersSETUsers.Status="遗留"WHEREUsers.ID<1000。无法否定,这种方法既便捷又直观,但是它有时也会参与不用要的步骤。例如,假设您须要先向某个表中拔出数据,而后经常使用UPDATE来更改数据,那么这便是两个独立的事务。不过,当你有数百万行数据时,此类徒增的额外事务就会发生少量不用要的操作。
关于一些大规模操作而言,更好的处置打算是:在查问中经常使用内联CASE语句,在拔出操作环节中设置列的值。如此,咱们便可以一次性性地同时处置初始拔出和修负数据了。
3.尽量缩小大表查问
就系统开支而言,关于任何体量数据表的查问,都不是收费的。而关于那些领有数亿、甚至数十亿行的数据表的查问,更是如此。为此,咱们须要尽或者地将那些关于大体量数据表的查问,兼并为起码的团圆操作。例如,假设咱们想对一个数据表先依照某一列启动查问,而后再依照另一列予以查问。那么咱们便可以首先将其兼并为一个查问,而后确保你后续要查问的列领有了笼罩索引(CoveringIndex)。
假设您发现自己必定从一张大的数据表中失掉相反的数据子集,并须要对其运转较小的查问,那么您可以将其子集耐久化到其余中央,并对其启动查问,从而为以后和后续其余操作提速。这也将引出下一项低劣通常。
4.为数据设置预分级(Pre-stage)
假定您或组织中的其他人经常须要口头报表或存储环节。而这些报表或存储环节又须要经过衔接几张大的数据表,来汇总少量的数据。那么,您与其每次都从新运转衔接,不如将其预分级到专门用于此目的的数据表中。据此,报表或程序便可以针对该表一次性性地独特成功其操作,从而为自己(和他人)节俭少量的上班。此外,假设您有足够的资源,而且数据库也能够提供支持的话,也可以经常使用内存表,来进一步成功减速。
5.分批启动删除和降级
试想,您须要在一张数十亿行级的数据表中肃清数百万行。只管最便捷的方法莫过于在事务中运转DELETE。但这样一来,整张表就会在此环节中被锁定,直至事务成功。
而复杂一些的方法是分批口头删除(或降级)操作。此类操作可以与其余事务交织启动。由于每个事务都会变得更小,更易于治理,因此其余事务也可以在该操作前后或操作时期见缝插针地口头。
在实践运行中,此举将成为义务队列的良好用例。它岂但可以跟踪跨会话操作的进展,而且准许其以低优先级的形态,在后盾被操作口头。
6.经常使用暂时表提高指针性能
有过开发阅历的程序员都知道:指针的经常使用会造成运行的速度变慢,甚至会阻碍到其余操作。与此同时,那些依赖指针的操作,简直都可以用其余方法来成功。因此,在大少数状况下,咱们应该防止经常使用指针。
话说回来,假设您由于某种要素不得不经常使用指针的话,暂时表则可以缩小由指针带来的性能疑问。例如,假设您须要遍历某个数据表,并依据计算结果更改某一列的话,则可以将待降级的候选数据放入暂时表中,用指针来遍历该暂时表,而后在一次性性的操作中,运行一切的降级。当然,此模式还可以将指针的某个处置分红多个批次。
7.经常使用表值(table-valued)函数而非标量(scalar)函数
由于标量函数可以将计算封装到相似存储环节的SQL代码段中,因此开发人员的通常做法是:将标量函数的结果作为SELECT查问中的某一列去前往。不过,您可以经常使用表值函数来启动替代,并在查问中经常使用CROSSLY来取得更好的性能。
8.经常使用分区以防止少量数据移动
SQLServerEnterprise提供了一种分区(partitioning)配置,可以将数据库表宰割成多个分区。也就是说,假设你有一张表须要经常归档到另一个表中,那么就可以防止经常使用INSERT/DELETE来移动数据,而间接经常使用SWITCH来替代。
咱们可以假想一个场景,假设有一张表须要每天都被清空至一张归档表中。那么,咱们就可以经常使用SWITCH,便捷地将日常表中的页面,调配到该归档表中,从而口头清空和复制操作。与手动复制和删除相比,该切换环节所需的时期要少得多。CathrineWilhelmsen提供了如何以这种模式经常使用分区的精彩教程,您可以经过链接--,启动参考。
9.经常使用存储环节提高性能,经常使用ORM带来便利
ORMs,即:对象相关映射器(object-relationalmappers)是一套能以编程的模式生成SQL代码的软件工具包。它们准许您经常使用运行程序的编程言语及其隐喻(Metaphors),来开发和保养查问。
由于ORM或者发生低效、有时甚至无法被代码优化,而备受诟病。同时,它们也会降落开发人员学习SQL、以及了解查问内容的踊跃性。许少数据库开发人员准则上并不青睐ORM,他们在须要经过手动编写查问,以取得最佳性能时,往往无所适从。
相反,关于经常被调用、须要良好性能、不常被更改、以及须要数据库剖析工具对性能启动检测的查问而言,经常使用存储环节是最为合理的。与暂时查问相比,大少数数据库更容易取得存储环节的汇总统计消息。数据库的查问布局器也更容易对存储环节启动优化。
不过,将更多的数据库逻辑移入存储环节的缺陷是:逻辑与数据库的耦合愈加严密。存储环节或者会从性能长处变为渺小的技术债(TechnicalDebt)。假设您后续预备迁徙到另一种数据库技术的话,那么更改ORM的指标会比重写一切存储环节要容易得多。毕竟运行程序的数据库局部的编写模式,与运行逻辑的耦合度不高。相反,ORM倒是能够使得编写和保养数据库代码愈加容易。此外,咱们可以审核由ORM生成的代码,以启动优化,而且查问缓存也能够准许咱们重用那些最常被生成的查问。
总之,假设您觉得运行程序端的可保养性更关键的话,那就请经常使用ORM;假设您须要在数据库方面具备更好的性能的话,则请经常使用存储环节。
译者引见
陈峻(JulianChen),51CTO社区编辑,具备十多年的IT名目实施阅历,擅长对内外部资源与危险实施管控,专一流传网络与消息安保常识与阅历。
原文题目: SQLunleashed:9waystospeedupyourSQLqueries ,作者:SerdarYegulalp
深入了解优化SQL查询-如何写出高性能SQL语句的具体分析
深入了解优化SQL查询-如何写出高性能SQL语句的具体分析:1、 首先要搞明白什么叫执行计划?执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 “全表扫描”方式。 可见,执行计划并不是固定的,它是“个性化的”。 产生一个正确的“执行计划”有两点很重要:(1) SQL语句是否清晰地告诉查询优化器它想干什么?(2) 查询优化器得到的数据库统计信息是否是最新的、正确的?2、 统一SQL语句的写法对于以下两句SQL语句,程序员认为是相同的,数据库查询优化器认为是不同的。 select*from dual select*From dual其实就是大小写不同,查询分析器就认为是两句不同的SQL语句,必须进行两次解析。 生成2个执行计划。 所以作为程序员,应该保证相同的查询语句在任何地方都一致,多一个空格都不行!3、 不要把SQL语句写得太复杂我经常看到,从数据库中捕捉到的一条SQL语句打印出来有2张A4纸这么长。 一般来说这么复杂的语句通常都是有问题的。 我拿着这2页长的SQL语句去请教原作者,结果他说时间太长,他一时也看不懂了。 可想而知,连原作者都有可能看糊涂的SQL语句,数据库也一样会看糊涂。 一般,将一个Select语句的结果作为子集,然后从该子集中再进行查询,这种一层嵌套语句还是比较常见的,但是根据经验,超过3层嵌套,查询优化器就很容易给出错误的执行计划。 因为它被绕晕了。 像这种类似人工智能的东西,终究比人的分辨力要差些,如果人都看晕了,我可以保证数据库也会晕的。 另外,执行计划是可以被重用的,越简单的SQL语句被重用的可能性越高。 而复杂的SQL语句只要有一个字符发生变化就必须重新解析,然后再把这一大堆废品塞在内存里。 可想而知,数据库的效率会何等低下。 4、 使用“临时表”暂存中间结果简化SQL语句的重要方法就是采用临时表暂存中间结果,但是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在tempdb中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能。 5、 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语句的负担。 一次解析,多次重用,是提高数据库效率的原则。 6、 绑定变量窥测事物都存在两面性,绑定变量对大多数OLTP处理是适用的,但是也有例外。 比如在where条件中的字段是“倾斜字段”的时候。 “倾斜字段”指该列中的绝大多数的值都是相同的,比如一张人口调查表,其中“民族”这列,90%以上都是汉族。 那么如果一个SQL语句要查询30岁的汉族人口有多少,那“民族”这列必然要被放在where条件中。 这个时候如果采用绑定变量@nation会存在很大问题。 试想如果@nation传入的第一个值是“汉族”,那整个执行计划必然会选择表扫描。 然后,第二个值传入的是“布依族”,按理说“布依族”占的比例可能只有万分之一,应该采用索引查找。 但是,由于重用了第一次解析的“汉族”的那个执行计划,那么第二次也将采用表扫描方式。 这个问题就是著名的“绑定变量窥测”,建议对于“倾斜字段”不要采用绑定变量。 7、 只在必要的情况下才使用begin tranSQL 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个空位,可以在insert发生时重新被利用。 在这种情况下由于有空位可以利用,就不会发生页分裂。 但是查询性能会比较低,因为查询时必须扫描那些没有数据的空位。 重建聚集索引后情况改变了,因为重建聚集索引就是把表中的数据重新排列一遍,原来的空位没有了,而页的填充率又很高,插入数据经常要发生页分裂,所以性能大幅下降。 对于聚集索引没有建在顺序字段上的表,是否要给与比较低的页填充率?是否要避免重建聚集索引?是一个值得考虑的问题!10、加nolock后查询经常发生页分裂的表,容易产生跳读或重复读加nolock后可以在“插、删、改”的同时进行查询,但是由于同时发生“插、删、改”,在某些情况下,一旦该数据页满了,那么页分裂不可避免,而此时nolock的查询正在发生,比如在第100页已经读过的记录,可能会因为页分裂而分到第101页,这有可能使得nolock查询在读101页时重复读到该条数据,产生“重复读”。 同理,如果在100页上的数据还没被读到就分到99页去了,那nolock查询有可能会漏过该记录,产生“跳读”。 上面提到的哥们,在加了nolock后一些操作出现报错,估计有可能因为nolock查询产生了重复读,2条相同的记录去插入别的表,当然会发生主键冲突。 11、使用like进行模糊查询时应注意有的时候会需要进行一些模糊查询比如select*from 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 JoinSQL 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,赶紧升级吧。
怎样提高SQL查询效率
1. SQL优化的原则是:将一次操作需要读取的BLOCK数减到最低,即在最短的时间达到最大的数据吞吐量。 调整不良SQL通常可以从以下几点切入: ? 检查不良的SQL,考虑其写法是否还有可优化内容 ? 检查子查询 考虑SQL子查询是否可以用简单连接的方式进行重新书写 ? 检查优化索引的使用 ? 考虑数据库的优化器 2. 避免出现SELECT * FROM table 语句,要明确查出的字段。 3. 在一个SQL语句中,如果一个where条件过滤的数据库记录越多,定位越准确,则该where条件越应该前移。 4. 查询时尽可能使用索引覆盖。 即对SELECT的字段建立复合索引,这样查询时只进行索引扫描,不读取数据块。 5. 在判断有无符合条件的记录时建议不要用SELECT COUNT (*)和select top 1 语句。 6. 使用内层限定原则,在拼写SQL语句时,将查询条件分解、分类,并尽量在SQL语句的最里层进行限定,以减少数据的处理量。 7. 应绝对避免在order by子句中使用表达式。 8. 如果需要从关联表读数据,关联的表一般不要超过7个。 9. 小心使用 IN 和 OR,需要注意In集合中的数据量。 建议集合中的数据不超过200个。 10. <> 用 < 、 > 代替,>用>=代替,<用<=代替,这样可以有效的利用索引。 11. 在查询时尽量减少对多余数据的读取包括多余的列与多余的行。 12. 对于复合索引要注意,例如在建立复合索引时列的顺序是F1,F2,F3,则在where或order by子句中这些字段出现的顺序要与建立索引时的字段顺序一致,且必须包含第一列。 只能是F1或F1,F2或F1,F2,F3。 否则不会用到该索引。 13. 多表关联查询时,写法必须遵循以下原则,这样做有利于建立索引,提高查询效率。 格式如下select sum() from table1 table1, table2 table2, table3 table3 where (table1的等值条件(=)) and (table1的非等值条件) and (table2与table1的关联条件) and (table2的等值条件) and (table2的非等值条件) and (table3与table2的关联条件) and (table3的等值条件) and (table3的非等值条件)。 注:关于多表查询时from 后面表的出现顺序对效率的影响还有待研究。 14. 子查询问题。 对于能用连接方式或者视图方式实现的功能,不要用子查询。 例如:select name from customer where customer_id in ( select customer_id from order where money>1000)。 应该用如下语句代替:select name from customer inner join order on _id=_id where >100。 15. 在WHERE 子句中,避免对列的四则运算,特别是where 条件的左边,严禁使用运算与函数对列进行处理。 比如有些地方 substring 可以用like代替。 16. 如果在语句中有not in(in)操作,应考虑用not exists(exists)来重写,最好的办法是使用外连接实现。 17. 对一个业务过程的处理,应该使事物的开始与结束之间的时间间隔越短越好,原则上做到数据库的读操作在前面完成,数据库写操作在后面完成,避免交叉。 18. 请小心不要对过多的列使用列函数和order by,group by等,谨慎使用disti软件开发t。 19. 用union all 代替 union,数据库执行union操作,首先先分别执行union两端的查询,将其放在临时表中,然后在对其进行排序,过滤重复的记录。 当已知的业务逻辑决定query A和query B中不会有重复记录时,应该用union all代替union,以提高查询效率。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。