1。首先我们要了解什么是执行计划?执行计划是数据库根据SQL语句和相关表的统计信息制定的查询计划。该计划由查询优化决定。例如,如果使用SQL语句从一个有100,000条记录的表中搜索一条记录,查询优化器将选择“索引搜索”方法。如果表被归档,只有5000条记录,查询优化器就会改变计划,采用“全表扫描”的方式。可见执行计划并不是固定的,是“个性化”的。生成正确的“执行计划”有两个要点:(1)SQL语句是否清楚地告诉查询优化器它要做什么?(2)查询优化器得到的数据库统计信息是否准确无误?2.统一SQL语句的写法对于下面的两条SQL语句,程序员认为它们是一样的,但是数据库查询优化器却认为它们是不同的。select*fromdualselect*Fromdual实际上是大小写不同,查询分析器认为是两条不同的SQL语句,必须解析两次。生成2个执行计划。所以,作为程序员,应该保证同一个查询语句处处一致,多一个空格也不行!3、SQL语句不要太复杂。2张A4纸那么长。一般来说,这种复杂的陈述通常是有问题的。我拿着2页长的SQL语句去问原作者,他说太长了,一时半会儿也看不懂。可想而知,即使是原作者看到的SQL语句也会乱,数据库也会乱。一般比较常见的是将一条Select语句的结果作为一个子集,然后从这个子集开始查询。但是根据经验,如果超过3层以上的嵌套,查询优化器很容易给出错误的执行计划。因为惊呆了。像人工智能这样的事情最终比人类的解决方案更糟糕。人晕了,我敢保证数据库也会晕。另外,执行计划是可以复用的,SQL语句越简单,被复用的可能性就越高。而复杂的SQL语句只要改变一个字符就必须重新解析,然后这堆垃圾就塞进了内存。可以想象数据库将是多么低效。4、使用“临时表”临时存放中间结果。简化SQL语句的一个重要方法是使用临时表来临时存储中间结果。然而,临时表的好处远不止这些。临时结果临时存放在临时表中,后续查询会在tempdb中,这样可以避免程序中多次扫描主表,也大大减少程序执行时“共享锁”阻塞“更新锁”,减少阻塞和提高并发性能。5、OLTP系统SQL语句必须使用绑定变量select*fromorderheaderwherechangetime>'2010-10-2000:00:01'select*fromorderheaderwherechangetime>'2010-09-2200:00:01'以上两条语句,查询优化器thinks是不同的SQL语句,需要解析两次。如果使用绑定变量select*fromorderheaderwherechangetime>@chgtime@chgtime,可以传入任意值,这样大量类似的查询可以复用执行计划,可以大大减轻数据库解析SQL语句的负担。一次解析,多次复用是提高数据库效率的原则。6.使用绑定变量监视事物有两个方面。绑定变量适用于大多数OLTP流程,但也有例外。例如,当where条件中的字段为“倾斜字段”时。“倾斜字段”是指该列中的大部分值都是相同的。例如,在一份人口普查表格中,“种族”一栏中90%以上是汉族。所以如果一条SQL语句要查询30岁的汉族人口,那么where条件中就必须要有“ethnicity”这一列。这时候如果使用绑定变量@nation,就会出现很大的问题。试想一下,如果@nation传入的第一个值是“汉”,那么整个执行计划必然会选择表扫描。然后,第二个值作为“Boyei”传入。按理说,“博奕”的比例可能只有万分之一,应该用索引搜索。但是,由于复用了第一次解析的“汉族”的执行计划,所以第二次也会使用表扫描的方式。这个问题就是著名的“bindvariablesnooping”,建议不要对“slantedfields”使用绑定变量。7.仅在必要时才使用begintranSQL。SQLServer中一条SQL语句默认是一个事务,语句执行后默认是提交的。实际上,这是begintran的一种最小化形式,就像在每个句子的开头隐含了begintran,在末尾隐含了commit。在某些情况下,我们需要显式声明begintran。例如,在执行“插入、删除、修改”操作时,需要同时修改几个表。begintran可以起到这样的作用,它可以把几条SQL语句一起执行,最后一起commit。好处是保证了数据的一致性,但没有什么是完美的。Begintran付出的代价是,所有被SQL语句锁定的资源,直到commit被commit后才能释放。可见,如果被Begintran困住的SQL语句过多,数据库的性能就会很差。在大事务提交之前,其他的语句必然会被阻塞,从而产生大量的块。Begintran使用的原理是在保证数据一致性的前提下,begintran困住的SQL语句越少越好!在某些情况下,可以使用触发器来同步数据,不一定要使用begintran。8、一些SQL查询语句要加nolock。在SQL语句中加入nolock是提高SQLServer并发性能的重要手段。在oracle中没有必要这样做,因为oracle的结构比较合理,有undo表空间保存“数据伏笔”,如果修改的时候数据还没有commit,那你读的就是copy在它被修改之前,副本被放置在撤消表空间中。这样oracle的读写就可以相互独立了,这也是oracle被广泛推崇的原因。SQLServer的读写会互相阻塞。为了提高并发性能,可以对一些查询加nolock,这样读的时候可以允许写,但是缺点是可能读到未提交的脏数据。使用nolock有3个原则。(1)“插入、删除、修改”的查询结果不能加Nolock!(2)查询表属于频繁分页,慎用nolock!(3)使用临时表时也可以保存“数据前影”,类似于oracle的undo表空间功能,可以使用临时表提高并发性能,不要使用nolock。9.未建聚簇索引在表的order字段上,表容易出现分页,比如order表有订单号orderid和客户号contactid,那么应该在哪个字段加聚簇索引呢?对于这张表,ordernumber是顺序添加的,如果在orderid上添加了聚簇索引,那么新的行会添加到最后,这样就不会经常出现分页的情况,但是由于大部分的查询都是基于customerid的,所以只在contactid上加一个聚簇索引是有道理的,对于订单表来说,contactid不是一个顺序字段,比如“张三”的“contactid”是001,那么“张三”的订单信息肯定放在这个表的第一个数据页。如果“张三”??放置了一个n今天的新订单,应该是订单信息不能放在表格的最后一页,而是放在第一页!如果第一页已满怎么办?抱歉,必须将此表中的所有数据移回以为此记录腾出空间。SQLServer的索引与Oracle的索引不同。SQLServer的聚集索引实际上是按照聚集索引字段的顺序对表进行排序,相当于Oracle的索引组织表。SQLServer的聚集索引是表本身的一种组织形式,所以效率非常高。也正是因为如此,在插入一条记录的时候,它的位置并不是随便放的,而是必须放在本该按顺序放置的数据页中。如果该数据页没有空间,则会导致页面拆分。所以很明显,聚簇索引不是建立在表的顺序字段上的,表很容易出现页分裂。曾经遇到过一个情况,小伙伴对某张表重建索引后,插入效率明显下降。估计大概是这样的。表的聚集索引可能不是建立在表的顺序字段上的,而且表经常被归档,所以表的数据以稀疏状态存在。比如张三下了20单,但是最近3个月只有5单。归档策略是保留3个月的数据。那么张三过去的15个命令都被存档了,还剩下15个空位,可以插进去,等出现的时候再用。在这种情况下,不会发生页面拆分,因为有可用的空缺。但是查询性能会比较低,因为查询必须扫描那些没有数据的空槽。重建聚簇索引后情况发生了变化,因为重建聚簇索引就是对表中的数据进行重新排列,原来的空位没有了,而且页面的填充率非常高,插入数据时经常出现分页,所以性能显着下降。对于聚集索引不是建立在顺序字段上的表,是否应该给一个比较低的pagefillrate?您想避免重建聚簇索引吗?是值得思考的问题!10.加了nolock后,查询经常有分页的表,很容易跳读或者重复读。加了nolock之后,就可以在“插入、删除、修改”的同时进行查询了。但是,因为“插入、删除、修改”是同时发生的,所以在某些情况下,一旦数据页满了,那么Pagesplitting是不可避免的,此时就发生了nolock查询。比如100页已经读过的记录,由于分页,可能会被分到101页,这样在读101页的时候,可能会导致nolock查询重复读,这块数据就产生了“repeatedread”。同理,如果100页的数据还没有被读取就赋值给了99页,那么nolock查询可能会漏掉记录,导致“跳读”。上面提到的哥们,加了nolock之后,有些操作报错。估计是因为nolock查询可能会出现重复读。如果将两条相同的记录插入到其他表中,当然会发生主键冲突。11、使用like进行模糊查询时,需要注意一些模糊查询,比如select*fromcontactwhereusernamelike'%yue%'keyword%yue%,因为yue前面用了"%",所以查询一定要到全表扫描,除非必要,关键字前不要加%,12、数据类型隐式转换对查询效率的影响在sqlserver2000的数据库中,我们的程序在提交sql语句时,没有使用强类型提交该字段值,数据类型由sqlserver2000自动转换,会导致传入参数与主键字段类型不一致。这时候sqlserver2000可能会使用全表扫描。Sql2005上没有发现这种问题,但还是要注意一下。13、SQLServer表的三种连接方式(1)MergeJoin(2)NestedLoopJoin(3)HashJoinSQLServer2000只有一种连接方式——NestedLoopJoin。如果A的结果集较小,则作为默认值从表面上看,A中的每条记录都要在B中扫描,实际扫描的行数等于A结果集中的行数xB结果集中的行数。因此,如果两个结果集都很大,则Join的结果不好。SQLServer2005添加了MergeJoin。如果表A和表B的join字段刚好是聚簇索引所在的字段,那么表的顺序已经排好了,只要把两边放在一起,这次join的开销就相当于A表的结果集的行数加上B表的结果集的行数,一为加法,一为乘法。可以看出mergejoin的效果比NestedLoopJoin好很多。如果join的字段上没有索引,SQL2000的效率是相当低的,但是SQL2005提供了Hashjoin,相当于给A表和B表的结果集临时加了索引,所以SQL2005的效率相比与SQL2000。我认为这是一个重要的原因。综上所述,连接表时需要注意以下几点:(1)连接字段尽量选择聚簇索引所在的字段(2)慎重考虑where条件,最小化A和的结果集B表(3)如果join很多连接字段缺少索引,你还在用SQLServer2000,赶紧升级吧。
