1.尽可能避免在WHERE子句中对字段进行NULL值判断sql
在WHERE子句中对字段进行NULL值判断会致使引擎放弃使用索引而进行全表扫描。如:数据库
SELECT id FROM t WHERE num IS NULL
能够在num字段设置默认值0,确保表中 num字段没有NULL值,而后这样查询:编程
SELECT id FROM t WHERE num=0
2.尽可能避免在WHERE子句中使用!=或<>操做符服务器
在WHERE子句中使用!=或<>操做符将致使引擎放弃使用索引而进行全表扫描。优化器将没法经过索引来肯定将要命中的行数,所以须要搜索该表的全部行。并发
3.尽可能避免在WHERE子句中使用OR来链接条件函数
在WHERE子句中使用OR链接条件,将会致使引擎放弃使用索引而进行全表扫描,如:post
SELECT id FROM t WHERE num=10 OR num=20
能够改为:性能
SELECT id FROM t WHERE num=10 UNION ALL SELECT id FROM t WHERE num=20
4.慎用IN和NOT IN大数据
由于IN会使系统没法使用索引,而只能直接搜索表中的数据。如:优化
SELECT id FROM t WHERE num in(1,2,3)
对于连续的数值,能用BETWEEN就不要用IN
SELECT id FROM t WHERE num BETWEEN 1 AND 3
5.尽可能避免在索引过的字符数据中,使用'%'开头搜索。
此状况下引擎没法利用索引,如:
SELECT * FROM t WHERE name Like '%Jhon%'
建议使用全文检索。
6.必要时强制查询优化器使用某个索引
如在WHERE子句中使用参数,也会致使全表扫描。由于SQL只有在运行时才会解析局部变量,但优化程序不能讲访问计划的选择推迟到运行时,它必须在编译时进行选择。然而,若是在编译时简历访问计划,变量的值仍是未知的,于是没法做为索引选择的输入项。以下面语句会进行全表扫描:
SELECT id FROM t WHERE num = @num
能够改成强制查询使用索引:
SELECT id FROM t WITH(INDEX(索引名)) WHERE num=@num
7.尽可能避免在WHERE子句中对字段进行表达式操做
这将致使引擎放弃使用索引而进行全表扫描。如:
SELECT * FROM t WHERE num/2=100
应改成:
SELECT * FROM t WHERE num=100*2
SELECT* FROM t WHERE SUBSTRING(num,1,4)='5678'
应改成:
SELECT * FROM t WHERE num LIKE '5678%'
SELECT * FROM t WHERE DATEDIFF(yy,num,GETDATE())>21
应改成:
SELECT * FROM t WHERE num<DATEADD(yy,-21,GETDATE())
即任何对列的操做都将致使表扫描,包括数据库函数、计算表达式等;查询时要尽量将操做移至右边。
8.尽可能避免在WHERE子句中对字段进行函数操做
会致使引擎放弃使用索引而进行全表扫描,如:
SELECT id FROM t WHERE SUBSTRING(name,1,3)='abc' SELECT id FROM t WHERE DATEDIFF(day,createdate,'2015-01-30')=0
应改成:
SELECT id FROM t WHERE name like 'abc%' SELECT id FROM t WHERE createdate>='2015-01-30' AND createdate<'2015-01-31'
9.不要在WHERE子句中的"="左边进行函数、运算符或其余表达式运算
该操做会致使系统可能没法正确使用索引。
10.若是索引是复合索引,要用该索引的第一个字段做为条件
在使用索引字段做为条件时,若是该索引是符合索引,则必须使用该索引中的第一个字段做为条件时才能保证系统使用该索引,不然该索引将不会被使用。且应尽量让字段顺序与索引顺序一致。
11.不少时候EXISTS是个好的选择
SELECT num FROM t1 WHERE num in(SELECT num FROM t2)
用下面的语句替换:
SELECT num FROM t1 WHERE EXISTS(SELECT 1 FROM t2 WHERE num=t1.num)
SELECT SUM(t1.c1) FROM t1 WHERE (SELECT COUNT(*) FROM t2 WHERE t2.c2=t1.c2>0)
用下面的语句替换:
SELECT SUM(t1.c1) FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c2=t1.c2)
二者产生相同的结果,可是后者的效率明显高于前者。由于后者不会产生大量锁定的表扫描或索引扫描。
若是想校验表里是否存在某条记录,不要用COUNT(*),那样效率很低且浪费服务器资源。
能够用EXISTS代替,如:
IF (SELECT COUNT(*) FROM t WHERE name='xxx')
能够改为:
IF EXISTS(SELECT * FROM t WHERE name='xxx')
常常须要写一个T-SQL语句比较一个父结果集和子结果集,从而找到是否存在在父结果集中有而在子结果集中没有的记录,如:
SELECT t1.a FROM t1 WHERE NOT EXISTS(SELECT * FROM tb WHERE t1.a=t2.a) SELECT t1.a FROM t1 LEFT JOIN tb ON t1.a=t2.b WHERE t2.a IS NULL SELECT a FROM t1 WHERE a NOT IN(SELECT a FROM t2)
三种写法均可以获得相同的结果,可是效率一次下降。
12.尽可能使用表变量代替临时表。
若是表变量包含大量数据,请注意索引很是有限(只有主键索引)
13.避免频繁建立和删除临时表,以减小系统表资源消耗
14.合理利用临时表
适当使用临时表能够提升效率,例如,当须要重复引用大型表或经常使用表中某个数据集时,可是,对于一次性事件,最好使用导出表。
15.临时表的SELECT INTO和CREATE TABLE
在新建临时表时,若是一次性插入数据量很大,可使用SELECT INTO代替CREATE TABLE,避免形成大量log,以提升速度;若是数据量不大,为了缓和系统表资源,应先CREATE TABLE再INSERT。
16.若是使用到临时表,在存储过程的最后务必将全部的临时表显式删除
先TRUNCATE TABLE,而后DROP TABLE,这样能够避免系统表长时间锁定。
17.SET NOCOUNT ON 和SET NOCOUNT OFF
在全部的存储过程和触发器的开始处设置SET NOCOUNT ON,在结束时设置SET NOCOUNT OFF。无需再执行存储过程和触发器的每一个语句后向客户端发送DONE_IN_PROC消息。
18.尽可能避免大事务操做,提升系统并发能力。
19.尽可能避免向客户端返回大数据量
若数据量过大,应该考虑相应需求是否合理。
20.避免使用不兼容的数据类型
例如FLOAT和INT,CHAR和VARCHAR,BINARY和VARBINARY是不兼容的。数据类型的不兼容可能使优化器没法执行一些原本能够进行的优化操做。例如:
SELECT name FROM employee WHERE salary > 6000
在这条语句中,如salary字段是MONEY型的,则优化器很难对其进行优化,由于6000是整形。咱们应当在编程时将整形转换为MONEY类型,而不要等到运行时转化。
21.充分利用链接条件
在某些状况下,两个表之间可能不止一个链接条件,这时在WHERE子句中将链接条件完整地写上,有可能大大提升查询速度。如:
SELECT SUM(account.amount) FROM account,card WHERE account.card_no=card.card_no SELECT SUM(account.amount) FROM account,card WHERE account.card_no=card.card_no AND account.account_no=card.account_no
第二句将比第一句执行快得多
22.使用视图加速查询
把表的一个子集进行排序并建立视图,有时能加速查询。它有助于比偶棉多重排序操做,并且在其余方面黑能简化优化器的工做。如:
SELECT cust.name,rcvbles.balance,...other columns FROM cust,rcvbles WHERE cust.customer_id=rcvbles.customer_id AND rcvbles.balance>0 AND cust.postcode>'98000' ORDER BY cust.name
若是这个查询要被执行屡次而不止一次,能够把全部未付款的客户找出来放在一个视图中,并按客户的名字进行排序:
CREATE VIEW v_cust_rcvbles AS SELECT cust.name,rcvbles.balance,...other columns FROM cust,rcvbles WHERE cust.customer_id=rcvbles.customer_id AND rcvbles.balance>0 ORDER BY cust.name
而后如下面的方式在视图中查询:
SELECT * FROM v_cust_rcvbles WHERE postcode>'98000'
视图中的行要比主表中的行少,并且物理顺序就是所要求的顺序,减小了磁盘I/O,因此查询工做量能够获得大幅减小。
23.能用DISTINCT的就不用GROUP BY
SELECT id FROM products WHERE price>10 GROUP BY id
可改成:
SELECT DISTINCT id FROM products WHERE price>10
24.能用UNION ALL就不要用UNION
UNION ALL不执行SELECT DISTINCT函数,这样就会减小不少没必要要的资源
25.尽可能不要用SELECT INTO语句
SELECT INTO语句会致使表锁定,阻止其余用户访问该表。
26.并非全部索引对查询都有效
SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如表中有字段sex,male,female几乎各一半,那么即便在sex上创建了索引也对查询效率起不了做用。
27.索引并非越多越好
索引当然能够提升相应的SELECT效率,但同时也下降了INSERT和UPDATE的效率,由于INSERT或UPDATE时有可能会重建索引,因此怎样建索引须要慎重考虑。一个表的索引数量最好不要超过6个。
28.尽量避免更新CLUSTERED索引数据列
由于CLUSTERED索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将致使整个表记录的顺序的调整,会耗费至关大的资源。若应用系统须要频繁更新CLUSTERED索引数据列,那么须要考虑是否应该将该索引建为CLUSTERED索引。
29.尽可能使用数字型字段
若只含数值信息的字段尽可能不要设计为字符型,这会下降查询和链接的性能,并会增长存储开销。这是由于引擎在处理查询和链接时会逐个比较字符串中每一个字符,而对于数字型而言只须要比较一次。
30.尽量使用VARCHAR/NVARCHAR替代CHAR/NCHAR
由于首先变长字段存储空间小,能够节省存储空间,其次对于查询来讲,在一个相对较小的字段内搜索效率显然要高。
31.不要使用SELECT * FROM
用具体字段列表代替"*",不要返回任何用不到的字段
32.尽可能避免使用游标
游标效率较差,若是游标操做的数据超过1万行就应该考虑改写
33.尝试使用基于集的方法
在使用基于游标的方法或临时表以前,先寻找基于集的解决方案