一、影响速度的因素
- 没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)
I/O
吞吐量小,形成了瓶颈效应- 没有创建计算列导致查询不优化
- 内存不足
- 网络速度慢
- 查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)
- 锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)
sp_lock
,sp_who
,活动的用户查看,原因是读写竞争资源- 返回了不必要的行和列
- 查询语句不好,没有优化
二、查询优化方法
- 把数据、日志、索引放到不同的
I/O
设备上,增加读取速度,以前可以将Tempdb
应放在RAID0
上,SQL2000不在支持。数据量(尺寸)越大,提高I/O
越重要 - 纵向、横向分割表,减少表的尺寸(
sp_spaceuse
) - 升级硬件
- 根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段
- 提高网速
- 扩大服务器的内存。配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。可考虑将虚拟内存大小设置为计算机中安装的物理内存的1.5倍。如果另外安装了全文检索功能,并打算运行
Microsoft
搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的3倍。将SQL Server max server memory
服务器配置选项配置为物理内存的1.5倍(虚拟内存大小设置的一半) - 增加服务器
CPU
个数;但是必须明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是SQL Server自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询的排序、连接、扫描和GROUP BY
字句同时执行,SQL Server根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU
的查询最适合并行处理。但是更新操作UPDATE
,INSERT
,DELETE
还不能并行处理 - 如果是使用
LIKE
进行查询的话,简单的使用INDEX
是不行的,但是全文索引耗空间。LIKE 'a%'
使用索引LIKE '%a'
不使用索引用LIKE '%a%'
查询时,查询耗时和字段值总长度成正比,所以不能用CHAR
类型,而用VARCHAR
。对于字段的值很长的建全文索引 DB Server
和Application Server
分离,OLTP
和OLAP
分离- 分布式分区视图可用于实现数据库服务器联合体。联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层WEB站点的处理需要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件【分区视图】)
- 在实现分区视图之前,必须先水平分区表
- 在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。
- 重建索引
DBCC REINDEX
,DBCC INDEXDEFRAG
,收缩数据和日志DBCC SHRINKDB
,DBCC SHRINKFILE
设置自动收缩日志对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。在T-SQL
的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:
- 查询语句的词法、语法检查
- 将语句提交给DBMS的查询优化器
- 优化器做代数优化和存取路径的优化
- 由预编译模块生成查询规划
- 然后在合适的时间提交给系统处理执行
- 最后将执行结果返回给用户,其次,看一下SQL Server的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照
B树
存放。
COMMIT
和ROLLBACK
的区别。ROLLBACK
回滚所有的事物,COMMIT
提交当前的事物。没有必要在动态SQL里写事物,如果要写请写在外面,如:begin tran exec(@s) commit trans
或者将动态SQL写成函数或者存储过程。- 在查询
SELECT
语句中用WHERE
字句限制返回的行数,避免表扫描,如果返回不必要的数据,浪费了服务器的I/O
资源,加重了网络的负担降低性能。如果表很大,在表扫描的期间将表锁住,禁止其他的联接访问表,后果严重。 - SQL的注释申明对执行没有任何影响
- 尽可能不使用光标,它占用大量的资源。如果需要
ROW-BY-ROW
地执行,尽量采用非光标技术,如:在客户端循环,用临时表,TABLE
变量,用子查询,用CASe
语句等等。游标可以按照它所支持的提取选项进行分类:只进必须按照从第一行到最后一行的顺序提取行。FETCHNEXT
是唯一允许的提取操作,也是默认方式。可滚动性可以在游标中任何地方随机提取任意行。游标的技术在SQL2000下变得功能很强大,他的目的是支持循环。有四个并发选项:
READ_ONLY
:不允许通过游标定位更新(UPDATE),且在组成结果集的行中没有锁。OPTIMISTIC WITH VALUES
:乐观并发控制是事务控制理论的一个标准部分。乐观并发控制用于这样的情形,即在打开游标及更新行的间隔中,只有很小的机会让第二个用户更新某一行。当某个游标以此选项打开时,没有锁控制其中的行,这将有助于最大化其处理能力。如果用户试图修改某一行,则此行的当前值会与最后一次提取此行时获取的值进行比较。如果任何值发生改变,则服务器就会知道其他人已更新了此行,并会返回一个错误。如果值是一样的,服务器就执行修改。选择这个并发选项OPTIMISTIC WITH ROW VERSIONING
:此乐观并发控制选项基于行版本控制。使用行版本控制,其中的表必须具有某种版本标识符,服务器可用它来确定该行在读入游标后是否有所更改。
在SQL Server中,这个性能由timestamp
数据类型提供,它是一个二进制数字,表示数据库中更改的相对顺序。每个数据库都有一个全局当前时间戳值:@@DBTS
。每次以任何方式更改带有timestamp
列的行时,SQL Server先在时间戳列中存储当前的@@DBTS
值,然后增加@@DBTS
的值。如果某个表具有timestamp
列,则时间戳会被记到行级。服务器就可以比较某行的当前时间戳值和上次提取时所存储的时间戳值,从而确定该行是否已更新。服务器不必比较所有列的值,只需比较timestamp
列即可。如果应用程序对没有timestamp
列的表要求基于行版本控制的乐观并发,则游标默认为基于数值的乐观并发控制。SCROLL LOCKS
:这个选项实现悲观并发控制。在悲观并发控制中,在把数据库的行读入游标结果集时,应用程序将试图锁定数据库行。在使用服务器游标时,将行读入游标时会在其上放置一个更新锁。如果在事务内打开游标,则该事务更新锁将一直保持到事务被提交或回滚;当提取下一行时,将除去游标锁。如果在事务外打开游标,则提取下一行时,锁就被丢弃。因此,每当用户需要完全的悲观并发控制时,游标都应在事务内打开。更新锁将阻止任何其它任务获取更新锁或排它锁,从而阻止其它任务更新该行。
然而,更新锁并不阻止共享锁,所以它不会阻止其它任务读取行,除非第二个任务也在要求带更新锁的读取。滚动锁根据在游标定义的SELECT
语句中指定的锁提示,这些游标并发选项可以生成滚动锁。滚动锁在提取时在每行上获取,并保持到下次提取或者游标关闭,以先发生者为准。下次提取时,服务器为新提取中的行获取滚动锁,并释放上次提取中行的滚动锁。滚动锁独立于事务锁,并可以保持到一个提交或回滚操作之后。如果提交时关闭游标的选项为关,则COMMIT
语句并不关闭任何打开的游标,而且滚动锁被保留到提交之后,以维护对所提取数据的隔离。所获取滚动锁的类型取决于游标并发选项和游标SELECT
语句中的锁提示。
锁提示 | 只读 | 乐观数值 | 乐观行版本控制 | 锁定 |
---|---|---|---|---|
无提示 | 未锁定 | 未锁定 | 未锁定 | 更新 |
NOLOCK | 未锁定 | 未锁定 | 未锁定 | 未锁定 |
HOLDLOCK | 共享 | 共享 | 共享 | 更新 |
UPDLOCK | 错误 | 更新 | 更新 | 更新 |
TABLOCKX | 错误 | 未锁定 | 未锁定 | 更新 |
其它 | 未锁定 | 未锁定 | 未锁定 | 更新 |
指定
NOLOCK
提示将使指定了该提示的表在游标内是只读的。
- 用
Profiler
来跟踪查询,得到查询所需的时间,找出SQL的问题所在;用索引优化器优化索引 - 注意
UNION
和UNION ALL
的区别。UNION
自动去重,效率低。 - 注意使用
DISTINCT
,在没有必要时不要用,它同UNION
一样会使查询变慢。重复的记录在查询里是没有问题的 - 查询时不要返回不需要的行和列
- 用
sp_configure
、SET QUERY_GOVERNOR_COST_LIMIT
或者SET QUERY_GOVERNOR_COST_LIMIT
来限制查询消耗的资源。当评估查询消耗的资源超出限制时,服务器自动取消查询,在查询之前就扼杀掉。SET LOCK_TIMEOUT
设置锁的时间 - 用
SELECT TOP 100/10 PERCENT
来限制用户返回的行数或者SET ROWCOUNT
来限制操作的行 - 在SQL2000以前,一般不要用如下的字句:
"IS NULL","<>","!=","!>","!<","NOT","NOT EXISTS","NOT IN","NOT LIKE","LIKE '%500'"
,因为他们不走索引全是表扫描。也不要在WHERE
字句中的列名加函数,如CONVERT
,SUBSTRING
等,如果必须用函数的时候,创建计算列再创建索引来替代,还可以变通写法:WHERE SUBSTRING(firstname,1,1)='m'
改为WHERE firstname LIKE 'm%'
(索引扫描),一定要将函数和列名分开。并且索引不能建得太多和太大。NOT IN
会多次扫描表,使用EXISTS
、NOT EXISTS
、IN
、LEFT OUTER JOIN
来替代,特别是左连接,而EXISTS
比IN
更快,最慢的是NOT
操作,如果列的值含有空,以前它的索引不起作用,现在2000的优化器能够处理了。相同的是IS NULL
,NOT
,NOT EXISTS
,NOT IN
能优化它,而”<>”等还是不能优化,用不到索引。 - 使用
Query Analyzer
,查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20%的代码占据了80%的资源,我们优化的重点是这些慢的地方。 - 如果使用了
IN
或者OR
等时发现查询没有走索引,使用显示申明指定索引:SELECT * FROM PersonMember(INDEX=IX_Title) WHERE processid IN('男','女')
- 将需要查询的结果预先计算好放在表中,查询的时候再
SELECT
- MIN()和MAX()能使用到合适的索引
- 数据库有一个原则是代码离数据越近越好,所以优先选择
Default
,依次为Rules
,Triggers
,Constraint
(约束如外健主健Check/UNIQUE……,数据类型的最大长度等等都是约束),PROCEDURE
这样不仅维护工作小,编写程序质量高,并且执行的速度快。 - 如果要插入大的二进制值到
Image
列,使用存储过程,千万不要用内嵌INSERT
来插入。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值,存储过程就没有这些动作,方法:CREATE PROCEDURE p_insert AS INSERT INTO TABLE(Fimage)VALUES(@image)
,在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。 BETWEEN
在某些时候比IN
速度更快,BETWEEN
能够更快地根据索引找到范围。用查询优化器可见到差别。SELECT * FROM chineseresume WHERE title IN ('男','女')
与SELECT * FROM chineseresume WHERE BETWEEN '男' AND '女'
是一样的。由于IN
会在比较多次,所以有时会慢些。- 在必要是对全局或者局部临时表创建索引,有时能够提高速度,但不是一定会这样,因为索引也耗费大量的资源。他的创建同是实际表一样。
- 不要建没有作用的事物例如产生报表时,浪费资源。只有在必要使用事物时使用它。
- 用
OR
的字句可以分解成多个查询,并且通过UNION
连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用UNION ALL
执行的效率更高,多个OR
的字句没有用到索引,改写成UNION
的形式再试图与索引匹配。一个关键的问题是否用到索引。 - 尽量少用视图,它的效率低。对视图操作比直接对表操作慢,可以用
STOREDPROCEDURE
来代替它。特别是不要用视图嵌套,增加了寻找原始资料的难度。我们看视图的本质:它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时,不要使用指向多个表的视图,直接从表检索或者仅仅包含这个表的视图上读,否则增加了不必要的开销,查询受到干扰,为了加快视图的查询,SQL Server增加了视图索引的功能。 - 没有必要时不要用
DISTINCT
和ORDERBY
,这些动作可以改在客户端执行。它们增加了额外的开销。这同UNION
和UNIONALL
一样的道理。SELECT TOP 20 ad.companyname,comid,position,ad.referenceid,worklocation,CONVERT(VARCHAR(10),ad.postDate,120) AS postDate1,workyear,degreeDESCription FROM COMPANYAD_query ad WHERE referenceID IN (JCNAD00329667,JCNAD132168,JCNAD00337748,JCNAD00338345,JCNAD00333138,JCNAD00303570,JCNAD00303569,JCNAD00303568,JCNAD00306698,JCNAD00231935,JCNAD00231933,JCNAD00254567,JCNAD00254585,JCNAD00254608,JCNAD00254607,JCNAD00258524,JCNAD00332133,JCNAD00268618,JCNAD00279196,JCNAD00268613) ORDER BY postdate DESC
- 在
IN
后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断的次数 - 当用
SELECT INTO
时,它会锁住系统表(sysobjects
,sysindexes
等等),阻塞其他的连接的存取。创建临时表时用显示申明语句,而不是SELECT INTO droptablet_lxhbegintran SELECT * INTO t_lxh FROM chineseresume WHERE name='XYZ'
。在另一个连接中SELECT * FROM sysobjects
可以看到SELECT INTO
会锁住系统表,CREATE TABLE
也会锁系统表(不管是临时表还是系统表)。所以千万不要在事物内使用它!!!这样的话如果是经常要用的临时表请使用实表,或者临时表变量。 - 一般在
GROUP BY
个HAVING
字句之前就能剔除多余的行,所以尽量不要用它们来做剔除行的工作。他们的执行顺序应该如下最优:SELECT
的WHERE
字句选择所有合适的行,GROUP BY
用来分组个统计行,HAVING
字句用来剔除多余的分组。这样GROUP BY
个HAVING
的开销小,查询快,对于大的数据行进行分组和HAVING
十分消耗资源。如果GROUP BY
的目的不包括计算,只是分组,那么用DISTINCT
更快 - 一次更新多条记录比分多次更新每次一条快,就是说批处理好
- 少用临时表,尽量用结果集和
TABLE
类性的变量来代替它,TABLE
类型的变量比临时表好 - 在SQL2000下,计算字段是可以索引的,需要满足的条件如下:
- 计算字段的表达是确定的
- 不能用在
TEXT
,Ntext
,Image
数据类型- 必须配制如下选项
ANSI_NULLS=ON,ANSI_PADDINGS=ON
- 尽量将数据的处理工作放在服务器上,减少网络的开销,如使用存储过程。存储过程是编译好、优化过、并且被组织到一个执行规划里、且存储在数据库中的SQL语句,是控制流语言的集合,速度当然快。反复执行的动态SQL,可以使用临时存储过程,该过程(临时表)被放在
Tempdb
中。以前由于SQL Server对复杂的数学计算不支持,所以不得不将这个工作放在其他的层上而增加网络的开销。SQL2000支持UDFs
,现在支持复杂的数学计算,函数的返回值不要太大,这样的开销很大。用户自定义函数象光标一样执行的消耗大量的资源,如果返回大的结果采用存储过程 - 不要在一句话里再三的使用相同的函数,浪费资源,将结果放在变量里再调用更快
SELECT COUNT(*)
的效率教低,尽量变通他的写法,而EXISTS
快,同时请注意区别:SELECT COUNT(Field of null) FROM TABLE
和SELECT COUNT(Field of not null) FROM TABLE
的返回值是不同的。- 当服务器的内存够多时,【配制线程数量=最大连接数+5】,这样能发挥最大的效率;否则使用【配制线程数量<最大连接数】,启用SQL Server的线程池来解决,如果还是【数量=最大连接数+5】,严重的损害服务器的性能。
- 按照一定的次序来访问你的表。如果你先锁住表A,再锁住表B,那么在所有的存储过程中都要按照这个顺序来锁定它们。如果你(不经意的)某个存储过程中先锁定表B,再锁定表A,这可能就会导致一个死锁。如果锁定顺序没有被预先详细的设计好,死锁很难被发现
- 通过
SQL Server Performance Monitor
监视相应硬件的负载Memory:PageFaults/sec
计数器如果该值偶尔走高,表明当时有线程竞争内存。如果持续很高,则内存可能是瓶颈。Process
:
%DPCTime
指在范例间隔期间处理器用在缓延程序调用(DPC)接收和提供服务的百分比。(DPC正在运行的为比标准间隔优先权低的间隔)。由于DPC
是以特权模式执行的,DPC
时间的百分比为特权时间百分比的一部分。这些时间单独计算并且不属于间隔计算总数的一部分。这个总数显示了作为实例时间百分比的平均忙时。%ProcessorTime
计数器 如果该参数值持续超过95%,表明瓶颈是CPU
。可以考虑增加一个处理器或换一个更快的处理器。%PrivilegedTime
指非闲置处理器时间用于特权模式的百分比。(特权模式是为操作系统组件和操纵硬件驱动程序而设计的一种处理模式。它允许直接访问硬件和所有内存。另一种模式为用户模式,它是一种为应用程序、环境分系统和整数分系统设计的一种有限处理模式。操作系统将应用程序线程转换成特权模式以访问操作系统服务)。特权时间的%
包括为间断和DPC
提供服务的时间。特权时间比率高可能是由于失败设备产生的大数量的间隔而引起的。这个计数器将平均忙时作为样本时间的一部分显示。%UserTime
表示耗费CPU
的数据库操作,如排序,执行Aggregate Functions
等。如果该值很高,可考虑增加索引,尽量使用简单的表联接,水平分割大表格等方法来降低该值。PhysicalDisk
:Curretn Disk Queue Length
计数器该值应不超过磁盘数的1.5~2倍。要提高性能,可增加磁盘。SQLServer:Cache Hit Ratio
计数器该值越高越好。如果持续低于80%,应考虑增加内存。注意该参数值是从SQL Server启动后,就一直累加记数,所以运行经过一段时间后,该值将不能反映系统当前值。
- 分析
SELECT emp_name FROM employee WHERE salary > 3000
在此语句中若salary
是FLOAT
类型的,则优化器对其进行优化为CONVERT(FLOAT,3000)
,因为3000是个整数,我们应在编程时使用3000.0而不要等运行时让DBMS进行转化。同样字符和整型数据的转换。
三、千万级数据优化
- 对查询进行优化,应尽量避免全表扫描,首先应考虑在
where
及order by
涉及的列上建立索引。 - 应尽量避免在
where
子句中对字段进行null
值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null
可以在num
上设置默认值0,确保表中num
列没有null
值,然后这样查询:select id from t where num=0
- 应尽量避免在
where
子句中使用!=
或<>
操作符,否则引擎将放弃使用索引而进行全表扫描。 - 应尽量避免在
where
子句中使用or
来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如: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
in
和not in
也要慎用,否则会导致全表扫描,如:select id from t where num in(1,2,3)
对于连续的数值,能用between
就不要用in
了:select id from t where num between 1 and 3
- 下面的查询也将导致全表扫描:
select id from t where name like '%李%'
若要提高效率,可以考虑全文检索。 - 如果在
where
子句中使用参数,也会导致全表扫描。因为SQL
只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然``而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:select id from t where num=@num
可以改为强制查询使用索引:select id from t with(index(索引名)) where num=@num
- 应尽量避免在
where
子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:select id from t where num/2=100
应改为:select id from t where num=100*2
- 应尽量避免在
where
子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:select id from t where substring(name,1,3)='abc'
,name
以abc
开头的id
应改为:select id from t where name like 'abc%'
- 不要在
where
子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。 - 在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
- 不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:create table #t(…)
- 很多时候用
exists
代替in
是一个好的选择:select num from a where num in(select num from b)
用下面的语句替换:select num from a where exists(select 1 from b where num=a.num)
- 并不是所有索引对查询都有效,
SQL
是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL
查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex
上建了索引也对查询效率起不了作用。 - 索引并不是越多越好,索引固然可以提高相应的
select
的效率,但同时也降低了insert
及update
的效率,因为insert
或update
时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。 - 应尽可能的避免更新
clustered
索引数据列,因为clustered
索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新clustered
索引数据列,那么需要考虑是否应将该索引建为clustered
索引。 - 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
- 尽可能的使用
varchar/nvarchar
代替char/nchar
,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。 - 任何地方都不要使用
select * from t
,用具体的字段列表代替“*”,不要返回用不到的任何字段。 - 尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
- 避免频繁创建和删除临时表,以减少系统表资源的消耗。
- 临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
- 在新建临时表时,如果一次性插入数据量很大,那么可以使用
select into
代替create table
,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table
,然后insert
。 - 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先
truncate table
,然后drop table
,这样可以避免系统表的较长时间锁定。 - 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
- 使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
- 与临时表一样,游标并不是不可使用。对小型数据集使用
FAST_FORWARD
游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。 - 在所有的存储过程和触发器的开始处设置
SET NOCOUNT ON
,在结束时设置SET NOCOUNT OFF
。无需在执行存储过程和触发器的每个语句后向客户端发送DONE_IN_PROC
消息。 - 尽量避免大事务操作,提高系统并发能力。
- 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。