说起SQL SERVER的调优,我想大伙也很想知道这方面的知识。本人也正在探索的路上,大家有什么好的意见,欢迎一起探讨、研究。博取众人之长,才能扬长避短。
本文中的内容主要是摘自《程序员的SQL金典》,如若大家想拜读,可在网上下载拜读(当然最好的方式还是购买作者的书)。
关于调优的方案,有涉及硬件方面的知识,也有涉及软件方面的知识。但本人只是个软件方面的IT男,所以只是记录软件方面的内容。
其实关于SQL SERVER或者是其它数据库来讲,有些优化手段都是一致的。比如常规的方式有如下几种方式:

创建必要的索引

大学读书时就听说过数据库里面的索引,一直没去深究过,也在无知无畏中进行了四年多的开发生涯,想来惭愧的很,今天有幸了解,顿感人生之阔然开朗一般。索引,不单是数据库里面才有,像我们写代码不也碰到数组也有索引嘛。索引就是已经按照某一种固定好的方式排序好内容,然后我们再去通过索引位置来定位到它。说到SQL SERVER的索引,有必要讲讲两个概念。分别是聚簇索引和非聚簇索引。
1、聚簇索引:就是索引存储的方式跟内容物理存储的方式一致
2、非聚簇索引:就是索引存储的方式跟内容物理存储的顺序不一致
下面简单说明两者的区别。最简单的例子就是汉语词典的方式,对于一本汉语词典来说,它的物理存储顺序是已经固定好了的,是通过拼音的顺序排列好的,这也就是说,汉语词典的物理存储方式就是通过拼音的方式来存储。比如以拼音来建立的索引,就是聚簇索引。WHY?因为索引的存储跟汉语词典的物理存储是一致的,也就是上面概念所说的分类。那么非聚簇索引是什么情况,比如像汉语词典里面偏旁部首是一种索引的话,那么偏旁部首的索引就会是非聚簇索引了。WHY?因为它跟汉语词典原有的物理存储方式不一致。当创建聚簇索引时需要每一张表只能有一个聚簇索引,因为表中数据的物理顺序只能有一个,而非聚集索引则可以创建多个。

注:由于索引需要占据一定的存储空间,而且索引也会降低数据插入、更新和删除的速度,所以应该只创建必要的索引,一般是在检索的字段创建索引。 对于删除来说,索引会造成碎片的问题。因为当我们删除一条记录的时候,对应的索引并不会删除。造成创建的索引被闲置,一旦闲置的索引碎片多了,就会影响查询的效率。系统的垃圾碎片也是一样的道理,需要定期清除。对于索引来说,发现使用索引搜索的速度慢了,就需要定期去重建索引,重建索引将会先删除之前创建的索引,然后再新建新的索引,主流数据库管理系统都提供了重建索引的功能。

使用预编译查询

很多人会使用存储过程把SQL语句预先编译起来,以此来达到优化的目的。有的项目是根据用户的输入来动态执行SQL语句,不管何种方式,都使用参数化的方式来执行,这样不仅可以避免SQL注入漏洞攻击,最重要数据库会对这些参数化SQL执行预编译,这样第一次执行的时候DBMS会为这个SQL语句进行查询优化并且执行预编译,这样以后再执行这个SQL 的时候就直接使用预编译的结果,这样可以大大提高执行的速度。

调整WHERE 子句中的连接顺序(这个不是很懂?)

DBMS 一般采用自下而上的顺序解析WHERE 子句,根据这个原理,表连接最好写在其他WHERE条件之前,那些可以过滤掉最大数量记录。
比如下面的SQL语句性能较差:

SELECT [FName],[FCity]
,[FAge]
,[FSalary]
FROM T_Person
WHERE FSalary > 50000
AND FPosition= ‘MANAGER’
AND 25 < (SELECT COUNT(FName) FROM T_Manager
WHERE FManagerId=2);

我们将子查询的条件放到最前面,下面的SQL语句性能比较好:

SELECT [FName],[FCity]
,[FAge]
,[FSalary]
FROM T_Person
WHERE
25 < (SELECT COUNT(FName) FROM T_Manager
WHERE FManagerId=2)
AND FSalary > 50000
AND FPosition= ‘MANAGER’ ;
SELECT语句中避免使用'*'

对于SELECT * FROM TABLE这种方式,我想很多人都会这么去查询。WHY?一方面是因为系统的数据量级别还比较低,二来也图方便。但是随着项目的数据量疯长,系统的性能急速下降之后,优化的每一种方式都需要引起我们的重视。像查询这种方式来讲,如果不用检索出所有列的情况,尽量指定查询的列。这能有效减轻网络的负载和服务器资源的消耗。即使确实需要检索所有列,也不要使用SELECT *,因为这是一个非常低效的方法,DBMS在解析的过程中,会将*依次转换成所有的列名,这意味着将耗费更多的时间。在SQL Server Management Studio工具里面查询图省事,数据量不大我觉得还是可以的:),但至少在代码里面是不建议这么处理。

多条SQL语句压缩到一句SQL中去执行

对于SQL语句的执行,每次都要建立网络连接、进行权限校验、进行SQL语句的查询优化、发送执行结果,这个过程是非常耗时的,因此应该尽量避免过多的执行SQL语句,能够压缩到一句SQL执行的语句就不要用多条来执行。

使用表的别名

当在 SQL 语句中连接多个表时,请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减少那些由列名歧义引起的语法错误。

用EXISTS替代IN   (这个是否就没有使用IN的必要了?)

在查询中,为了满足一个条件,往往需要对另一个表进行联接,在这种情况下,使用EXISTS而不是IN 通常将提高查询的效率,因为IN 子句将执行一个子查询内部的排序和合并。下面的语句2 就比语句1 效率更加高。

语句 1:

SELECT  [FName],[FSalary],[FNumber] FROM T_Employee
WHERE FNumber> 0
AND FDEPTNO IN (SELECT FNumber
FROM T_Department
WHERE FMangerName = 'Tome')

语句 2:

SELECT [FName],[FSalary],[FNumber] FROM T_Employee
WHERE FNumber > 0
AND EXISTS (SELECT 1
FROM T_Department
WHERE T_Department. FDEPTNO = T_Employee.FNumber
AND FMangerName = ‘MELB’)
用表连接替换EXISTS  (这个不是很懂,不知道执行方式是怎么样的?感觉都一样)

通常来说,表连接的方式比EXISTS 更有效率,因此如果可能的话尽量使用表连接替换EXISTS。下面的语句2 就比语句1 效率更加高。
语句 1:

SELECT FName FROM T_Employee
WHERE EXISTS
(
SELECT 1 FROM T_Department
WHERE T_Employee.FDepartNo= FNumber
AND FKind='A'
);

语句 2:

SELECT FName FROM T_Department, T_Employee
WHERE T_Employee. FDepartNo = T_Departmen. FNumber
AND FKind = ‘A’ ;
避免在索引列上使用计算

在 WHERE 子句中,如果索引列是计算或者函数的一部分,DBMS 的优化器将不会使用索引而使用全表扫描。
例如下面的SQL语句用于检索月薪的12倍大于两万五千元的员工:

SELECT [TName],[FSalary] FROM T_Employee
WHERE FSalary * 12 >25000;

由于在大于号左边的是FSalary与12 的成绩表达式,这样DBMS 的优化器将不会使用字段FSalary的索引,因为DBMS必须对T_Employee表进行全表扫描,从而计算FSalary * 12 的值,然后与25000 进行比较。将上面的SQL语句修改为下面的等价写法后DBMS将会使用索引查找,从而大大提高了效率:

SELECT [FName],[FSalary],[FNumber] FROM T_Employee
WHERE FSalary >25000/12;

同样的,不能在索引列上使用函数,因为函数也是一种计算,会造成全表扫描。下面的语句2就比语句1 效率更加高。
语句 1:

SELECT [FAmount],[FName] FROM T_Example
WHERE ABS(FAmount)=300

语句2:

SELECT [FAmount],[FName] FROM T_Example
WHERE FAmount=300 OR FAmount=-300
用UNION ALL 替换UNION

当 SQL 语句需要UNION 两个查询结果集合时,即使检索结果中不会有重复的记录,如果使用UNION这两个结果集同样会尝试进行合并,然后在输出最终结果前进行排序。因此,如果检索结果中不会有重复的记录的话,应该用UNION ALL替代UNION,这样效率就会因此得到提高。下面的语句2 就比语句1效率更加高。
语句 1:

SELECTACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS1
WHERE TRAN_DATE = ''
UNION
SELECTACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS2
WHERE TRAN_DATE =''

语句2:

SELECTACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS1
WHERE TRAN_DATE =''
UNION ALL
SELECTACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS2
WHERE TRAN_DATE = ''
避免隐式类型转换造成的全表扫描

T_Person 表的字符串类型字段FLevel 为人员的级别,在FAge 字段上建有索引。我们执行下面的SQL语句用于检索所有级别等于10的员工:

SELECT FId,FAge,FName
FROM T_Person
WHERE FAge=10

在这个SQL 语句中,将字符串类型字段FLevel 与数值10 进行比较,由于在大部分数据库中隐式转换类型中数值类型的优先级高于字符串类型,因此DBMS会对FAge字段进行隐式类型转换,相当于执行了下面的SQL语句:

SELECT FId,FAge,FName
FROM T_Person
WHERE TO_INT(FAge)=10

由于在索引字段上进行了计算,所以造成了索引失效而使用全表扫描。因此应将SQL语句做如下修改:

SELECT FId,FAge,FName
FROM T_Person
WHERE FAge=''
防止检索范围过宽

如果DBMS 优化器认为检索范围过宽,那么它将放弃索引查找而使用全表扫描。下面是几种可能造成检索范围过宽的情况:
使用IS NOT NULL或者不等于判断,可能造成优化器假设匹配的记录数太多。
使用LIKE 运算符的时候,"a%"将会使用索引,而"a%c"和"%c"则会使用全表扫描,因为"a%c"和"%c"不能被有效的评估匹配的数量。

SQL SERVER调优常用方法的更多相关文章

  1. SQL SERVER调优常用方法 sql优化

    说起SQL SERVER的调优,我想大伙也很想知道这方面的知识.本人也正在探索的路上,大家有什么好的意见,欢迎一起探讨.研究.博取众人之长,才能扬长避短.本文中的内容主要是摘自<程序员的SQL金 ...

  2. SQL Server调优系列基础篇

    前言 关于SQL Server调优系列是一个庞大的内容体系,非一言两语能够分析清楚,本篇先就在SQL 调优中所最常用的查询计划进行解析,力图做好基础的掌握,夯实基本功!而后再谈谈整体的语句调优. 通过 ...

  3. SQL Server调优系列基础篇(常用运算符总结——三种物理连接方式剖析)

    前言 上一篇我们介绍了如何查看查询计划,本篇将介绍在我们查看的查询计划时的分析技巧,以及几种我们常用的运算符优化技巧,同样侧重基础知识的掌握. 通过本篇可以了解我们平常所写的T-SQL语句,在SQL ...

  4. SQL Server调优系列基础篇(联合运算符总结)

    前言 上两篇文章我们介绍了查看查询计划的方式,以及一些常用的连接运算符的优化技巧,本篇我们总结联合运算符的使用方式和优化技巧. 废话少说,直接进入本篇的主题. 技术准备 基于SQL Server200 ...

  5. SQL Server调优系列基础篇(并行运算总结)

    前言 上三篇文章我们介绍了查看查询计划的方式,以及一些常用的连接运算符.联合运算符的优化技巧. 本篇我们分析SQL Server的并行运算,作为多核计算机盛行的今天,SQL Server也会适时调整自 ...

  6. SQL Server调优系列基础篇(并行运算总结篇二)

    前言 上一篇文章我们介绍了查看查询计划的并行运行方式. 本篇我们接着分析SQL Server的并行运算. 闲言少叙,直接进入本篇的正题. 技术准备 同前几篇一样,基于SQL Server2008R2版 ...

  7. SQL Server调优系列基础篇(索引运算总结)

    前言 上几篇文章我们介绍了如何查看查询计划.常用运算符的介绍.并行运算的方式,有兴趣的可以点击查看. 本篇将分析在SQL Server中,如何利用先有索引项进行查询性能优化,通过了解这些索引项的应用方 ...

  8. SQL Server调优系列基础篇(子查询运算总结)

    前言 前面我们的几篇文章介绍了一系列关于运算符的介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符.有兴 ...

  9. SQL Server调优系列进阶篇(查询优化器的运行方式)

    前言 前面我们的几篇文章介绍了一系列关于运算符的基础介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符. ...

随机推荐

  1. 北大ACM(POJ1013-Counterfeit Dollar)

    Question:http://poj.org/problem?id=1013 问题点:排除+验证. Memory: 244K Time: 16MS Language: C++ Result: Acc ...

  2. php如何判断当前的操作系统是linux还是windows

    php_uname()这个函数的意义在于返回当前PHP所运行的系统的信息,下面对这个函数详细说明一下: /** * 功能:返回当前PHP所运行的系统的信息 * @param string $mode ...

  3. C++ Priority Queues(优先队列) and C++ Queues(队列)

    C++优先队列类似队列, 但是在这个数据结构中的元素按照一定的断言排列有序. empty() 如果优先队列为空,则返回真 pop() 删除第一个元素 push() 加入一个元素 size() 返回优先 ...

  4. OpenGL第6、7讲小结

    因为内容比较多,所以只看了两讲(强行解释). 一讲讲了如何给各个面贴纹理,一讲讲了加光照和按键控制. 现在讲的都是给规则的面贴纹理,像正方形,刚好纹理图也是正方形,那像人物模型的衣服贴起来用代码控制得 ...

  5. 4_1 wp8数据绑定与独立存储空间[wp8特色开发与编程技巧]

    Wp8数据绑定与独立存储空间 数据绑定为基于 Silverlight 的应用程序提供了一种显示数据并与数据进行交互的简便方法. 数据的显示方式独立于数据的管理. UI 和数据对象之间的连接或绑定使数据 ...

  6. 【风马一族_git_github】github项目建成网站

    ---------------------------------------- 1)点击 Settings 2)上一个红色矩形是访问的网址,下一个红色矩形是用来设置页面相关的信息 3)页面信息的修改 ...

  7. WP开发笔记——控件倾斜效果

    创建一个基本的 Windows Phone 应用程序并添加 TiltEffect 类文件. 添加要倾斜的控件的分类. 全局应用 IsTiltEnabled 依赖项属性,以便为所有的指定控件提供倾斜功能 ...

  8. (转)Jmeter内存溢出处理方式记录

    方法一: 使用jmeter进行压力测试时 遇到一段时间后报内存溢 出outfmenmory错误,导致jmeter卡死了,先尝试在jmeter.bat中增加了JVM_ARGS="- Xmx20 ...

  9. jquery.fullCalendar官方文档翻译(一款小巧好用的日程管理日历, 可集成Google Calendar)

    1. 使用方式, 引入相关js, css后, $(‘#div_name’).fullCalendar({//options});  接受的是一个option对象 2. 普通属性 2.1. year, ...

  10. 文件墙 CFilewall

    文件墙 CFilewall 记于 2013-09-26 == @[代码] [C#] []WPF] #### 使用了一些公司的组件和放大,但是不多,可以单独抽取出来 --- 程序结构 - Control ...