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

1.首先要搞明白什么叫执行计划?

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

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

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

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

2.统一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语句,需要解析两次。如果采用绑定变量

  slect * from orderheader where changetime>@changetime

  @changetime变量可以传入任何值,这样大量的类似查询可以重用该执行计划了,这可以大大降低数据库解析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语句中加onlock是提高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的索引组织表。SQLServer的聚集索引就是表本身的一种组织形式,所以它的效率是非常高的。也正因为此,插入一条记录,它的位置不是随便放的,而是要按照顺序放在该放的数据页,如果那个数据页没有空间了,就引起了页分裂。所以很显然,聚集索引没有建在表的顺序字段上,该表容易发生页分裂。

  曾经碰到过一个情况,一位哥们的某张表重建索引后,插入的效率大幅下降了。估计情况大概是这样的。该表的聚集索引可能没有建在表的顺序字段上,该表经常被归档,所以该表的数据是以一种稀疏状态存在的。比如张三下过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 server2000自动转换数据类型,会导致传入的参数与主键字段类型不一致,这个时候sql server 2000可能就会使用全表扫描。Sql2005上没有发现这种问题,但是还是应该注意一下。

13.SQL Server表连接的三种方式

  (1)Merge Join

  (2)Nested Loop Join

  (3)Hash Join

  SQL Server2000只有一种join方式---Nested LoopJoin,如果A结果集较小,那就默认作为外边,A中每条记录都要去B中扫描一遍,实际扫过的行数相当于A结果集行数X B结果集行数。所以如果两个结果集都很大,那Join的结果很糟糕。

  SQL Server2005新增了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 Server2000,赶紧升级吧。

http://www.cnblogs.com/ATree/archive/2011/02/13/sql_optimize_1.html

时间: 2024-10-12 22:47:40

优化SQL查询:如何写出高性能SQL语句的相关文章

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

1. 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 “全表扫描”方式. 可见,执行计划并不是固定的,它是“个性化的”.产生一个正确的“执行计划”有两点很重要: (1)    SQL语句是否清晰地告诉查询优化器它想干什么? (2)

如何写出高性能SQL语句(文章摘自web开发者)

(声明:本文内容摘自web开发者,仅供收藏学习之用,如有侵权请作者联系博主,博主将在第一时间删除) 原文地址:http://www.admin10000.com/document/484.html 1.首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生欀如一条SQL语句如果用来从一个10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变

如何写出高性能的SQL语句(转)

高性能的SQL语句能提高页面的执行效率,让你的服务器运行的更为流畅,所以大家在以后的使用过程中,尽量的考虑到效率. (1)整合简单,无关联的数据库访问: 如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系) (2)删除重复记录: 最高效的删除重复记录方法 ( 因为使用了ROWID)例子: DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO = E.EMP

代码示例:一些简单技巧优化JavaScript编译器工作详解,让你写出高性能运行的更快JavaScript代码

告诉你一些简单的技巧来优化JavaScript编译器工作,从而让你的JavaScript代码运行的更快.尤其是在你游戏中发现帧率下降或是当垃圾回收器有大量的工作要完成的时候. 单一同态: 当你定义了一个两个参数的函数,编译器会接受你的定义,如果函数参数的类型.个数或者返回值的类型改变编译器的工作会变得艰难.通常情况下,单一同态的数据结构和个数相同的参数会让你的程序会更好的工作. function example(a, b) { // 期望a,b都为数值类型 console.log(++a * +

如何使用SQLAlchemy库写出防SQL注入的Raw SQL

Python阵营有很多操作数据库的开源库(安装pip后,可以借助"pip search mysql"查看可用的库列表),其中被使用最多的无疑是MySQLdb,这个库简单易上手.其偏底层的特性为开发者提供灵活性的同时,也对不少新手写出的DB操作代码提出了考验,因为它只支持raw sql,容易导致sql注入攻击. 鉴于此,很多库提供了ORM接口能力,借助OO思想,数据库中的表被映射为Python的类,类的对象代表数据表中的一行记录,所有的DB操作都通过对象方法调用来实现,这些调用在底层被自

Hibernate原生SQL查询多表关联,SQL语句要注意的问题

Hibernate原生SQL查询多表关联,SQL语句要注意的问题 @for&ever 2009-9-4 系统环境: MySQL5.1 Hibernate3.3 有如下的假定: 实体类 Question 和 Answer分别对应数据表 question 和answer. 并且表 question 和answer 的字段大部分都一样,字段数目也一样. 执行如下的操作: 1> 使用hibernate 使用原生SQL查询, Query q = session.createSQLQuery(sql).

如何写出高性能的sql语句?

(1) 选择最有效率的表名顺序(只在基于规则的优化器中有效): ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表. (2) WHERE子句中的连接顺序.: ORACLE采用自下而上的顺序解析WHER

如何写出高性能的SQL语句

1. 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条记录,那查询优化器会选择"索引查找"方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 "全表扫描"方式. 可见,执行计划并不是固定的,它是"个性化的".产生一个正确的"执行计划"有两点很重要:

db2 优化基础 查询执行时间最长的SQL

之前写过一个查询执行查看运行时间最长的应用,查询到的是,应用程序,例如我的程序,查到的是IIS,没什么用. select elapsed_time_min,appl_status,agent_id from sysibmadm.long_running_sql order by elapsed_time_min desc fetch first 5 rows only 查询执行时间最长的SQL,要使用快照. 首先执行下列语句,打开先关的开关: db2 UPDATE MONITOR SWITCHE