??xml version="1.0" encoding="utf-8" standalone="yes"?>
问题:
后台pȝ需要连接SQL SERVERQ瞬时数据操作可能会很大Q如同时有好几万数据要插入数据库Q但在插入数据的同时另外的线E可能还要访问本数据库,插入数据的操作优先比较低,其他的访问需要即时返回,想了很久不知道该怎么解决Q望高手赐教了?br />回答:
1. 提高服务器硬仉|?
2. 使用 replication 之类的同步技? 频J操作的表同步ؓ多䆾, 操作分散到q些同步的表?
3. 对于数据查询, 量使用 READ UNCOMMITTED 事务隔离U别, 以减锁的开销
===
可以使用快照隔离U别
===
--查询Ӟ使用
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
--?/span>
with(nolock)
--q且建好索引Q尽量减耗时查询
--多个服务器分担压?/span>
--提高g性能q是最耗成本但最有效的方法了
===
查询时候不上锁
select *from tb(nolock)
使用镜像复制{?d一些只ȝ数据库!
===
除了以上的方法之?
2005q可以采用row-versiong based的snapshot 事务隔离层?
建立snapshot databaseQ将查询、更CU不同操作隔?br />
作者:no_mIss QQ:34813284
旉Q?006.11.07 23:30:00
环境Qwin2003 + mssql2005
最qL整理下对MSSQL的一些理解与感悟Q却一直没有心思和旉写,晚上无事便写了一探索MSSQL执行计划,本文讲执行计划但不仅限于讲执行计划?
|上的SQL优化的文章实在是很多Q说实在的,我也曄到处找这L文章Q什么不要用IN了,什么OR了,什么AND了,很多很多Q还有很多h拿出仅几S甚至几MS的时间差的例子来证明着什?有点可笑)Q让许多Z知道其是对还是错。而SQL优化又是每个要与数据库打交道的程序员的必修课Q所以写了此文,与朋友们共勉?
谈到优化必然要涉及索引Q就像要讲锁必然要说事务一P所以你需要了解一下烦引,仅仅是烦引,p讲半天了Q所以烦引我׃说了(打很多字是很累的Q况且我也知之甚?Q可以去参考相关的文章Q这个网上资料比较多了?
今天来探索下MSSQL的执行计划,来让大家知道如何查看MSSQL的优化机Ӟ以此来优化SQL查询?/p>
--DROP TABLE T_UserInfo----------------------------------------------------
--建测试表
CREATE TABLE T_UserInfo
(
Userid varchar(20), UserName varchar(20),
RegTime datetime, Tel varchar(20),
)
--插入试数据
DECLARE @I INT
DECLARE @ENDID INT
SELECT @I = 1
SELECT @ENDID = 100 --在此处更改要插入的数据,重新插入之前要删掉所有数?/p>
WHILE @I <= @ENDID
BEGIN
INSERT INTO T_UserInfo
SELECT 'ABCDE'+CAST(@I AS VARCHAR(20))+'EF','?+CAST(@I AS VARCHAR(20)),
GETDATE(),'876543'+CAST(@I AS VARCHAR(20))
SELECT @I = @I + 1
END
--相关SQL语句解释
---------------------------------------------------------------------------
--集烦?/p>
CREATE CLUSTERED INDEX INDEX_Userid ON T_UserInfo (Userid)
--建非聚集索引
CREATE NONCLUSTERED INDEX INDEX_Userid ON T_UserInfo (Userid)
--删除索引
DROP INDEX T_UserInfo.INDEX_Userid
---------------------------------------------------------------------------
---------------------------------------------------------------------------
--昄有关由Transact-SQL 语句生成的磁盘活动量的信?/p>
SET STATISTICS IO ON
--关闭有关由Transact-SQL 语句生成的磁盘活动量的信?/p>
SET STATISTICS IO OFF
--昄[q回有关语句执行情况的详l信息,q估计语句对资源的需求]
SET SHOWPLAN_ALL ON
--关闭[q回有关语句执行情况的详l信息,q估计语句对资源的需求]
SET SHOWPLAN_ALL OFF
---------------------------------------------------------------------------
误住:SET STATISTICS IO ?SET SHOWPLAN_ALL 是互斥的?/p>
OKQ现在开始:
首先Q我们插?00条数?/p>
然后我写了一个查询语句:
SELECT * FROM T_UserInfo WHERE USERID='ABCDE6EF'
选中以上语句Q按Ctrl+LQ如下图
q就是MSSQL的执行计划:表扫描:扫描表中的行
然后我们来看该语句对IO的读写:
执行:SET STATISTICS IO ON
此时再执行该SQL:SELECT * FROM T_UserInfo WHERE USERID='ABCDE6EF'
切换到消失栏昄如下Q?/p>
?T_UserInfo'。扫描计?Q逻辑? ơ,物理? ơ,预读0 ơ?/p>
解释下其意思:
四个值分别ؓQ?/p>
执行的扫描次?
从数据缓存读取的|;
从磁盘读取的|;
行查询而放入缓存的|
重要Q如果对于一个SQL查询有多U写法,那么q四个g的逻辑?logical reads)军_了哪个是最优化的?/p>
接下来我们ؓ其徏一个聚集烦?/p>
执行CREATE CLUSTERED INDEX INDEX_Userid ON T_UserInfo (Userid)
然后再执行SELECT * FROM T_UserInfo WHERE USERID='ABCDE6EF'
切换到消息栏如下昄Q?/p>
?T_UserInfo'。扫描计?Q逻辑? ơ,物理? ơ,预读0 ơ?/p>
此时逻辑ȝ原来?变成2Q?/p>
说明我们又加了一个烦引页Q现在我们查询时Q逻辑d是要M?1索引?1数据?Q此时的效率q不如不建烦引?/p>
此时再选中查询语句Q然后再Ctrl+LQ如下图:
聚集索引查找Q扫描聚集烦引中特定范围的行
说明Q此时用了烦引?/p>
OK,到这里你应该已经知道初步知道MSSQL查询计划和如何查看对IO的读取消耗了吧!
接下来我们l:
现在我再把测试数据改变成1000?/p>
再执行SET STATISTICS IO ON,再执?/p>
SELECT * FROM T_UserInfo WHERE USERID='ABCDE6EF'
在不加聚集烦引的情况下:
?T_UserInfo'。扫描计?Q逻辑? ơ,物理? ơ,预读0 ơ?/p>
在加聚集索引的情况下QCREATE CLUSTERED INDEX INDEX_Userid ON T_UserInfo (Userid)
?T_UserInfo'。扫描计?Q逻辑? ơ,物理? ơ,预读0 ơ?/p>
(其实也就是说此时是读了一个烦引页Q一个数据页)
如此Q在数据量稍大时Q烦引的查询优势显C出来了?/p>
先小ȝ下:
当你构徏SQL语句Ӟ按Ctrl+L可以看到语句是如何执行Q是用烦引扫描还是表扫描Q?/p>
通过SET STATISTICS IO ON 来查看逻辑读,完成同一功能的不同SQL语句Q逻辑?/p>
小查询速度快(当然不要N个只有几百条记录的例子来反我)?/p>
我们再l深入:
OKQ现在我们再来看一ơ,我们换个SQL语句Q来看下MSSQL如何来执行的此SQL呢?
现在L索引QDROP INDEX T_UserInfo.INDEX_Userid
现在打开[昄语句执行情况的详l信息]QSET SHOWPLAN_ALL ON
然后再执行:SELECT * FROM T_UserInfo WHERE USERID LIKE 'ABCDE8%'
看结果栏Q结果中有些具体参数Q比如IO的消耗,CPU的消耗?/p>
在这里我们只看StmtText:
SELECT * FROM T_UserInfo WHERE USERID LIKE 'ABCDE8%'
|--Table Scan(OBJECT:([student].[dbo].[T_UserInfo]), WHERE:(like([T_UserInfo].[Userid], 'ABCDE8%', NULL)))
Ctrl+L看下此时的图行执行计划:
我再加上索引Q?/p>
先关闭:SET SHOWPLAN_ALL OFF
再执行:CREATE CLUSTERED INDEX INDEX_Userid ON T_UserInfo (Userid)
再开启:SET SHOWPLAN_ALL ON
再执行:SELECT * FROM T_UserInfo WHERE USERID LIKE 'ABCDE8%'
查看StmtText:
SELECT * FROM T_UserInfo WHERE USERID LIKE 'ABCDE8%'
|--Clustered Index Seek(OBJECT:([student].[dbo].[T_UserInfo].[INDEX_Userid]), SEEK:([T_UserInfo].[Userid] >= 'ABCDE8' AND [T_UserInfo].[Userid] < 'ABCDE9'), WHERE:(like([T_UserInfo].[Userid], 'ABCDE8%', NULL)) ORDERED FORWARD)Ctrl+L看下此时的图行执行计划:
Ctrl+L看下此时的图行执行计划:
在有索引的情况下Q我们再写一个SQLQ?/p>
SET SHOWPLAN_ALL ON
SELECT * FROM T_UserInfo WHERE LEFT(USERID,4)='ABCDE8%'
查看StmtText:
SELECT * FROM T_UserInfo WHERE LEFT(USERID,4)='ABCDE8%'
|--Clustered Index Scan(OBJECT:([student].[dbo].[T_UserInfo].[INDEX_Userid]), WHERE:(substring([T_UserInfo].[Userid], 1, 4)='ABCDE8%'))
Ctrl+L看下此时的图行执行计划:
我们再分别看一下三U情况下对IO的操?/p>
分别如下Q?/p>
W一U情况:?T_UserInfo'。扫描计?Q逻辑? ơ,物理? ơ,预读0 ơ?/p>
W二U情况:?T_UserInfo'。扫描计?Q逻辑? ơ,物理? ơ,预读0 ơ?/p>
W三U情况:?T_UserInfo'。扫描计?Q逻辑? ơ,物理? ơ,预读0 ơ?/p>
q说?
W一ơ是表扫描,扫了7,也就是全表扫?/p>
W二ơ是索引扫描Q扫?늃引,2|据页
W三ơ是索引扫描+表扫描,扫了1늃引,7|据页
[囑Ş界面也有对CPU和IO的消耗,也可以看出来哪个最?]
通过比较Q嘿嘿,很容易的看出Q第二种W三U写法在都有索引的情况下Qlike有效的用烦引,而left则不能,q样一个最单的优化的例子就出来了,哈哈?/p>
如果以上你都明白了,那么你可能已l对SQL的优化有初步新的x了,|上一堆堆的SQL优化的文章真的是那样吗?你自p试就知道了,而不必盲目去记那些东西,自己试试Q看看MSSQL到底是怎么来执行就明白了?/p>
在我丄例子中,用的是聚集烦引扫描,字段是字母加数字Q大家可以试试看U数字的、字母的、汉字的{等Q了解下MMSQL会如何改变SQL语句来利用烦引。然后再试试非聚集烦引是什么情况?用不用烦引和什么有养I子查询MSSQL是如何执行?IN用不用烦引,LIKE用不用烦引?函数用不用烦引?OR、AND、UNIONQ子查询呢?在这里我不一一去试l大家看了,只要知道了如何去看MSSQL的执行计划(囑Ş和文?Q很多事情就很明朗了?/p>
大ȝQ?/p>
实现同一查询功能的SQL写法可能会有多种Q如果判断哪U最优化Q如果仅仅是从时间上来测Q会受很多外界因素的影响Q而我们明白了MSSQL如何L行,通过IO逻辑诅R通过查看囄的查询计划、通过其优化后而执行的SQL语句Q才是优化SQL的真正途径?/p>
另外提醒下:数据量的多少有时会媄响MSSQL对同一U查询写法语句的执行计划Q这一点在非聚集烦引上特别明显Q还有就是在多CPU与单CPU下,在多用户q发情况下,同一写法的查询语句执行计划会有所不同Q这个就需要大家有Z去试验了(我也没有q方面的太多l验与大家分??/p>
--原著:Haiwer
q期因工作需要,希望比较全面的ȝ下SQL SERVER数据库性能优化相关的注意事,在网上搜索了一?发现很多文章,有的都列Z上百?但是仔细看发玎ͼ有很多似是而非或者过?可能对SQL SERVER6.5以前的版本或者ORACLE是适用?的信息,只好自己Ҏ以前的经验和试l果q行ȝ了?/p>
我始l认为,一个系l的性能的提高,不单单是试运行或者维护阶D늚性能调优的Q务,也不单单是开发阶D늚事情Q而是在整个Y件生命周期都需要注意,q行有效工作才能辑ֈ的。所以我希望按照软g生命周期的不同阶D|ȝ数据库性能优化相关的注意事V?/p>
一?nbsp; 分析阶段
一般来_在系l分析阶D往往有太多需要关注的地方Q系l各U功能性、可用性、可靠性、安全性需求往往吸引了我们大部分的注意力Q但是,我们必须注意Q性能是很重要的非功能性需求,必须Ҏpȝ的特点确定其实时性需求、响应时间的需求、硬件的配置{。最好能有各U需求的量化的指标?/p>
另一斚wQ在分析阶段应该Ҏ各种需求区分出pȝ的类型,大的斚wQ区分是OLTPQ联Z务处理系l)和OLAPQ联机分析处理系l)?/p>
二?nbsp; 设计阶段
设计阶段可以说是以后pȝ性能的关键阶D,在这个阶D,有一个关pd以后几乎所有性能调优的过E?#8212;数据库设计?/p>
在数据库设计完成后,可以q行初步的烦引设计,好的索引设计可以指导~码阶段写出高效率的代码Qؓ整个pȝ的性能打下良好的基?/p>
以下是性能要求设计阶段需要注意的Q?/p>
1?数据库逻辑设计的规范化
数据库逻辑设计的规范化是我们一般所说的范式Q我们可以这h单理解范式:
W?规范Q没有重复的l或多值的列,q是数据库设计的最低要求?
W?规范: 每个非关键字D必M赖于d键字Q不能依赖于一个组合式d键字的某些组成部分。消除部分依赖,大部分情况下Q数据库设计都应该达到第二范式?/p>
W?规范: 一个非关键字段不能依赖于另一个非关键字段。消除传递依赖,辑ֈW三范式应该是系l中大部分表的要求,除非一些特D作用的表?/p>
更高的范式要求这里就不再作介l了Q个为,如果全部辑ֈW二范式Q大部分辑ֈW三范式Q系l会产生较少的列和较多的表,因而减了数据冗余Q也利于性能的提高?/p>
2?合理的冗?/p>
完全按照规范化设计的pȝ几乎是不可能的,除非pȝ特别的小Q在规范化设计后Q有计划地加入冗余是必要的?/p>
冗余可以是冗余数据库、冗余表或者冗余字D,不同_度的冗余可以vC同的作用?/p>
冗余可以是ؓ了编E方便而增加,也可以是Z性能的提高而增加。从性能角度来说Q冗余数据库可以分散数据库压力,冗余表可以分散数据量大的表的q发压力Q也可以加快Ҏ查询的速度Q冗余字D可以有效减数据库表的q接Q提高效率?/p>
3?主键的设?/p>
主键是必要的QSQL SERVER的主键同时是一个唯一索引Q而且在实际应用中Q我们往往选择最的键组合作Z键,所以主键往往适合作ؓ表的聚集索引。聚集烦引对查询的媄响是比较大的Q这个在下面索引的叙q?/p>
在有多个键的表,主键的选择也比较重要,一般选择ȝ长度的键,的键的比较速度快,同时的键可以主键的B树结构的层次更少?/p>
主键的选择q要注意l合主键的字D|序,对于l合主键来说Q不同的字段ơ序的主键的性能差别可能会很大,一般应该选择重复率低、单独或者组合查询可能性大的字D|在前面?/p>
4?外键的设?/p>
外键作ؓ数据库对象,很多为麻烦而不用,实际上,外键在大部分情况下是很有用的Q理由是Q?/p>
外键是最高效的一致性维护方法,数据库的一致性要求,依次可以用外键、CHECKU束、规则约束、触发器、客LE序Q一般认为,L据越q的Ҏ效率高?/p>
谨慎使用U联删除和联更斎ͼU联删除和联更C为SQL SERVER 2000当年的新功能Q在2005作了保留Q应该有其可用之处。我q里说的谨慎Q是因ؓU联删除和联更新有些突破了传统的关于外键的定义Q功能有点太q强大,使用前必ȝ定自己已l把握好其功能范_否则Q联删除和U联更新可能让你的数据莫名其妙的被修Ҏ者丢失。从性能看联删除和U联更新是比其他Ҏ更高效的Ҏ?/p>
5?字段的设?/p>
字段是数据库最基本的单位,其设计对性能的媄响是很大的。需要注意如下:
A、数据类型尽量用数字型,数字型的比较比字W型的快很多?/p>
B?数据cd量,q里的尽量小是指在满_以预见的未来需求的前提下的?/p>
C?量不要允许NULLQ除非必要,可以用NOT NULL+DEFAULT代替?/p>
D、少用TEXT和IMAGEQ二q制字段的读写是比较慢的Q而且Q读取的Ҏ也不多,大部分情况下最好不用?/p>
E?自增字段要慎用,不利于数据迁UR?/p>
6?数据库物理存储和环境的设?/p>
在设计阶D,可以Ҏ据库的物理存储、操作系l环境、网l环境进行必要的设计Q得我们的pȝ在将来能适应比较多的用户q发和比较大的数据量?/p>
q里需要注意文件组的作用,适用文gl可以有效把I/O操作分散C同的物理盘Q提高ƈ发能力?/p>
7?pȝ设计
整个pȝ的设计特别是pȝl构设计Ҏ能是有很大影响的,对于一般的OLTPpȝQ可以选择C/Sl构、三层的C/Sl构{,不同的系l结构其性能的关键也有所不同?/p>
pȝ设计阶段应该归纳一些业务逻辑攑֜数据库编E实玎ͼ数据库编E包括数据库存储q程、触发器和函数。用数据库编E实C务逻辑的好处是减少|络量q可更充分利用数据库的预~译和缓存功能?/p>
8?索引的设?/p>
在设计阶D,可以Ҏ功能和性能的需求进行初步的索引设计Q这里需要根据预计的数据量和查询来设计烦引,可能与将来实际用的时候会有所区别?/p>
关于索引的选择Q应改主意:
A?nbsp; Ҏ数据量决定哪些表需要增加烦引,数据量小的可以只有主键?/p>
B?nbsp; Ҏ使用频率军_哪些字段需要徏立烦引,选择l常作ؓq接条g、筛选条件、聚合查询、排序的字段作ؓ索引的候选字Dc?/p>
C?nbsp; 把经怸起出现的字段l合在一Pl成l合索引Q组合烦引的字段序与主键一P也需要把最常用的字D|在前面,把重复率低的字段攑֜前面?/p>
D?nbsp; 一个表不要加太多烦引,因ؓ索引影响插入和更新的速度?/p>
三?nbsp; ~码阶段
~码阶段是本文的重点Q因为在设计定的情况下Q编码的质量几乎军_了整个系l的质量?/p>
~码阶段首先是需要所有程序员有性能意识Q也是在实现功能同时有考虑性能的思想Q数据库是能q行集合q算的工P我们应该量的利用这个工P所谓集合运实际是扚wq算Q就是尽量减在客户端进行大数据量的循环操作Q而用SQL语句或者存储过E代ѝ关于思想和意识,很难说得很清楚,需要在~程q程中来体会?/p>
下面|列一些编E阶D需要注意的事项Q?/p>
1?只返回需要的数据
q回数据到客L臛_需要数据库提取数据、网l传输数据、客L接收数据以及客户端处理数据等环节Q如果返回不需要的数据Q就会增加服务器、网l和客户端的无效力_Q其宛_是显而易见的Q避免这cM仉要注意:
A、横向来看,不要写SELECT *的语句,而是选择你需要的字段?/p>
B?U向来看Q合理写WHERE子句Q不要写没有WHERE的SQL语句?/p>
C?注意SELECT INTO后的WHERE子句Q因为SELECT INTO把数据插入到临时表,q个q程会锁定一些系l表Q如果这个WHERE子句q回的数据过多或者速度太慢Q会造成pȝ表长期锁定,诸塞其他q程?/p>
D、对于聚合查询,可以用HAVING子句q一步限定返回的行?/p>
2?量做重复的工?/p>
q一点和上一点的目的是一LQ就是尽量减无效工作,但是q一点的侧重点在客户端程序,需要注意的如下Q?/p>
A?nbsp; 控制同一语句的多ơ执行,特别是一些基数据的多ơ执行是很多E序员很注意的?/p>
B?nbsp; 减少多次的数据{换,也许需要数据{换是设计的问题,但是减少ơ数是程序员可以做到的?/p>
C?nbsp; 杜绝不必要的子查询和q接表,子查询在执行计划一般解释成外连接,多余的连接表带来额外的开销?/p>
D?nbsp; 合ƈ对同一表同一条g的多ơUPDATEQ比?/p>
UPDATE EMPLOYEE SET FNAME=’HAIWER’ WHERE EMP_ID=’ VPA30890F’
UPDATE EMPLOYEE SET LNAME=’YANG’ WHERE EMP_ID=’ VPA30890F’
q两个语句应该合q成以下一个语?/p>
UPDATE EMPLOYEE SET FNAME=’HAIWER’,LNAME=’YANG’
WHERE EMP_ID=’ VPA30890F’
E?nbsp; UPDATE操作不要拆成DELETE操作+INSERT操作的Ş式,虽然功能相同Q但是性能差别是很大的?/p>
F?nbsp; 不要写一些没有意义的查询Q比?/p>
SELECT * FROM EMPLOYEE WHERE 1=2
3?注意事务和锁
事务是数据库应用中和重要的工P它有原子性、一致性、隔L、持久性这四个属性,很多操作我们都需要利用事务来保证数据的正性。在使用事务中我们需要做到尽量避免死锁、尽量减阻塞。具体以下方面需要特别注意:
A、事务操作过E要量,能拆分的事务要拆分开来?/p>
B?事务操作q程不应该有交互Q因Z互等待的时候,事务q未l束Q可能锁定了很多资源?/p>
C?事务操作q程要按同一序讉K对象?/p>
D、提高事务中每个语句的效率,利用索引和其他方法提高每个语句的效率可以有效地减整个事务的执行旉?/p>
E?量不要指定锁类型和索引QSQL SERVER允许我们自己指定语句使用的锁cd和烦引,但是一般情况下QSQL SERVER优化器选择的锁cd和烦引是在当前数据量和查询条件下是最优的Q我们指定的可能只是在目前情况下更有Q但是数据量和数据分布在来是会变化的?/p>
F?查询时可以用较低的隔ȝ别,特别是报表查询的时候,可以选择最低的隔离U别Q未提交读)?/p>
4?注意临时表和表变量的用法
在复杂系l中Q时表和表变量很难避免Q关于时表和表变量的用法,需要注意:
A、如果语句很复杂Q连接太多,可以考虑用时表和表变量分步完成?/p>
B?如果需要多ơ用C个大表的同一部分数据Q考虑用时表和表变量暂存q部分数据?/p>
C?如果需要综合多个表的数据,形成一个结果,可以考虑用时表和表变量分步汇总这多个表的数据?/p>
D、其他情况下Q应该控制时表和表变量的用?/p>
E?关于临时表和表变量的选择Q很多说法是表变量在内存Q速度快,应该首选表变量Q但是在实际使用中发玎ͼq个选择主要考虑需要放在时表的数据量Q在数据量较多的情况下,临时表的速度反而更快?/p>
F?关于临时表生用SELECT INTO和CREATE TABLE + INSERT INTO的选择Q我们做q测试,一般情况下QSELECT INTO会比CREATE TABLE + INSERT INTO的方法快很多Q但是SELECT INTO会锁定TEMPDB的系l表SYSOBJECTS、SYSINDEXES、SYSCOLUMNSQ在多用户ƈ发环境下Q容易阻塞其他进E,所以我的徏议是Q在q发pȝ中,量使用CREATE TABLE + INSERT INTOQ而大数据量的单个语句使用中,使用SELECT INTO?/p>
G?nbsp; 注意排序规则Q用CREATE TABLE建立的时表Q如果不指定字段的排序规则,会选择TEMPDB的默认排序规则,而不是当前数据库的排序规则。如果当前数据库的排序规则和TEMPDB的排序规则不同,q接的时候就会出现排序规则的冲突错误。一般可以在CREATE TABLE建立临时表时指定字段的排序规则ؓDATABASE_DEFAULT来避免上q问题?/p>
5?子查询的用法
子查询是一?SELECT 查询Q它嵌套?SELECT、INSERT、UPDATE、DELETE 语句或其它子查询中。Q何允怋用表辑ּ的地斚w可以使用子查询?/p>
子查询可以我们的编E灵zdP可以用来实现一些特D的功能。但是在性能上,往往一个不合适的子查询用法会形成一个性能瓉?/p>
如果子查询的条g中用了其外层的表的字段Q这U子查询叫作相兛_查询。相兛_查询可以用IN、NOT IN、EXISTS、NOT EXISTS引入?/p>
关于相关子查询,应该注意Q?/p>
A、NOT IN、NOT EXISTS的相兛_查询可以改用LEFT JOIN代替写法。比如:
SELECT PUB_NAME
FROM PUBLISHERS
WHERE PUB_ID NOT IN
(SELECT PUB_ID
FROM TITLES
WHERE TYPE = 'BUSINESS')
可以改写成:
SELECT A.PUB_NAME
FROM PUBLISHERS A LEFT JOIN TITLES B
ON B.TYPE = 'BUSINESS' AND
A.PUB_ID=B. PUB_ID
WHERE B.PUB_ID IS NULL
SELECT TITLE
FROM TITLES
WHERE NOT EXISTS
(SELECT TITLE_ID
FROM SALES
WHERE TITLE_ID = TITLES.TITLE_ID)
可以改写成:
SELECT TITLE
FROM TITLES LEFT JOIN SALES
ON SALES.TITLE_ID = TITLES.TITLE_ID
WHERE SALES.TITLE_ID IS NULL
B?如果保证子查询没有重?QIN、EXISTS的相兛_查询可以用INNER JOIN 代替。比如:
SELECT PUB_NAME
FROM PUBLISHERS
WHERE PUB_ID IN
(SELECT PUB_ID
FROM TITLES
WHERE TYPE = 'BUSINESS')
可以改写成:
SELECT DISTINCT A.PUB_NAME
FROM PUBLISHERS A INNER JOIN TITLES B
ON B.TYPE = 'BUSINESS' AND
A.PUB_ID=B. PUB_ID
C?IN的相兛_查询用EXISTS代替Q比?/p>
SELECT PUB_NAME
FROM PUBLISHERS
WHERE PUB_ID IN
(SELECT PUB_ID
FROM TITLES
WHERE TYPE = 'BUSINESS')
可以用下面语句代替:
SELECT PUB_NAME
FROM PUBLISHERS
WHERE EXISTS
(SELECT 1
FROM TITLES
WHERE TYPE = 'BUSINESS' AND
PUB_ID= PUBLISHERS.PUB_ID)
D、不要用COUNT(*)的子查询判断是否存在记录Q最好用LEFT JOIN或者EXISTSQ比如有人写q样的语句:
SELECT JOB_DESC FROM JOBS
WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)=0
应该ҎQ?/p>
SELECT JOBS.JOB_DESC FROM JOBS LEFT JOIN EMPLOYEE
ON EMPLOYEE.JOB_ID=JOBS.JOB_ID
WHERE EMPLOYEE.EMP_ID IS NULL
SELECT JOB_DESC FROM JOBS
WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)<>0
应该ҎQ?/p>
SELECT JOB_DESC FROM JOBS
WHERE EXISTS (SELECT 1 FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)
6?慎用游标
数据库一般的操作是集合操作,也就是对由WHERE子句和选择列确定的l果集作集合操作Q游标是提供的一个非集合操作的途径。一般情况下Q游标实现的功能往往相当于客L的一个@环实现的功能Q所以,大部分情况下Q我们把游标功能搬到客户端?/p>
游标是把l果集放在服务器内存Qƈ通过循环一条一条处理记录,Ҏ据库资源Q特别是内存和锁资源Q的消耗是非常大的Q所以,我们应该只有在没有其他方法的情况下才使用游标?/p>
另外Q我们可以用SQL SERVER的一些特性来代替游标Q达到提高速度的目的?/p>
A、字W串q接的例?/p>
q是论坛l常有的例子Q就是把一个表W合条g的记录的某个字符串字D连接成一个变量。比如需要把JOB_ID=10的EMPLOYEE的FNAMEq接在一P用逗号q接Q可能最Ҏ惛_的是用游标:
DECLARE @NAME VARCHAR(20)
DECLARE @NAME VARCHAR(1000)
DECLARE NAME_CURSOR CURSOR FOR
SELECT FNAME FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID
OPEN NAME_CURSOR
FETCH NEXT FROM RNAME_CURSOR INTO @NAME
WHILE @@FETCH_STATUS = 0
BEGIN
SET @NAMES = ISNULL(@NAMES+’,’,’’)+@NAME
FETCH NEXT FROM NAME_CURSOR INTO @NAME
END
CLOSE NAME_CURSOR
DEALLOCATE NAME_CURSOR
可以如下修改Q功能相同:
DECLARE @NAME VARCHAR(1000)
SELECT @NAMES = ISNULL(@NAMES+’,’,’’)+FNAME
FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID
B?用CASE WHEN 实现转换的例?/p>
很多使用游标的原因是因ؓ有些处理需要根据记录的各种情况需要作不同的处理,实际上这U情况,我们可以用CASE WHEN语句q行必要的判断处理,而且CASE WHEN是可以嵌套的。比?
表结?
CREATE TABLE 料g?
料号 VARCHAR(30),
名称 VARCHAR(100),
d?nbsp; VARCHAR(20),
单位1 VARCHAR(20),
单位1参数 NUMERIC(18,4),
单位2 VARCHAR(20),
单位2参数 NUMERIC(18,4)
)
GO
CREATE TABLE 入库?
旉 DATETIME,
料号 VARCHAR(30),
单位 INT,
入库数量 NUMERIC(18,4),
损坏数量 NUMERIC(18,4)
)
GO
其中Q单位字D可以是0Q?Q?Q分别代表主单位、单?、单?Q很多计需要统一单位Q统一单位可以用游标实玎ͼ
DECLARE @料号 VARCHAR(30),
@单位 INT,
@参数 NUMERIC(18,4),
DECLARE CUR CURSOR FOR
SELECT 料号,单位 FROM 入库?WHERE 单位 <>0
OPEN CUR
FETCH NEXT FROM CUR INTO @料号,@单位
WHILE @@FETCH_STATUS<>-1
BEGIN
IF @单位=1
BEGIN
SET @参数=(SELECT 单位1参数 FROM 料g?WHERE 料号 =@料号)
UPDATE 入库?SET 数量=数量*@参数,损坏数量=损坏数量*@参数,单位=1 WHERE CURRENT OF CUR
END
IF @单位=2
BEGIN
SET @参数=(SELECT 单位1参数 FROM 料g?WHERE 料号 =@料号)
UPDATE 入库?SET 数量=数量*@参数,损坏数量=损坏数量*@参数,单位=1 WHERE CURRENT OF CUR
END
FETCH NEXT FROM CUR INTO @料号,@单位
END
CLOSE CUR
DEALLOCATE CUR
可以改写成:
UPDATE A SET
数量=CASE A.单位 WHEN 1 THEN A.数量*B. 单位1参数
WHEN 2 THEN A.数量*B. 单位2参数
ELSE A.数量
END,
损坏数量= CASE A.单位 WHEN 1 THEN A. 损坏数量*B. 单位1参数
WHEN 2 THEN A. 损坏数量*B. 单位2参数
ELSE A. 损坏数量
END,
单位=1
FROM入库?A, 料g?B
WHERE A.单位<>1 AND
A.料号=B.料号
C?变量参与的UPDATE语句的例?/p>
SQL ERVER的语句比较灵z,变量参与的UPDATE语句可以实现一些游标一L功能Q比如:
?/p>
SELECT A,B,C,CAST(NULL AS INT) AS 序号
INTO #T
FROM ?
ORDER BY A ,NEWID()
产生临时表后Q已l按照A字段排序Q但是在A相同的情况下是ؕ序的Q这时如果需要更改序号字Dؓ按照A字段分组的记录序P只有游标和变量参与的UPDATE语句可以实现了,q个变量参与的UPDATE语句如下Q?/p>
DECLARE @A INT
DECLARE @序号 INT
UPDATE #T SET
@序号=CASE WHEN A=@A THEN @序号+1 ELSE 1 END,
@A=A,
序号=@序号
D、如果必M用游标,注意选择游标的类型,如果只是循环取数据,那就应该用只q游标(选项FAST_FORWARDQ,一般只需要静态游标(选项STATICQ?/p>
E?注意动态游标的不确定性,动态游标查询的记录集数据如果被修改Q会自动h游标Q这样得动态游标有了不定性,因ؓ在多用户环境下,如果其他q程或者本w更改了U录Q就可能h游标的记录集?/p>
7?量使用索引
建立索引后,q不是每个查询都会用烦引,在用烦引的情况下,索引的用效率也会有很大的差别。只要我们在查询语句中没有强制指定烦引,索引的选择和用方法是SQLSERVER的优化器自动作的选择Q而它选择的根据是查询语句的条件以及相兌的统计信息,q就要求我们在写SQL语句的时候尽量得优化器可以使用索引?/p>
Z使得优化器能高效使用索引Q写语句的时候应该注意:
A、不要对索引字段q行q算Q而要惛_法做变换Q比?/p>
SELECT ID FROM T WHERE NUM/2=100
应改?
SELECT ID FROM T WHERE NUM=100*2
SELECT ID FROM T WHERE NUM/2=NUM1
如果NUM有烦引应改ؓ:
SELECT ID FROM T WHERE NUM=NUM1*2
如果NUM1有烦引则不应该改?/p>
发现q这L语句Q?/p>
SELECT q??金额 FROM l余?
WHERE 100*q??2007*100+10
应该改ؓQ?/p>
SELECT q??金额 FROM l余?
WHERE q?2007 AND
?10
B?不要对烦引字D进行格式{?/p>
日期字段的例子:
WHERE CONVERT(VARCHAR(10), 日期字段,120)=’2008-08-15’
应该改ؓ
WHERE日期字段?’2008-08-15’ AND 日期字段<’2008-08-16’
ISNULL转换的例子:
WHERE ISNULL(字段,’’)<>’’应改?WHERE字段<>’’
WHERE ISNULL(字段,’’)=’’不应修改
WHERE ISNULL(字段,’F’) =’T’应改? WHERE字段=’T’
WHERE ISNULL(字段,’F’)<>’T’不应修改
C?不要对烦引字D用函?/p>
WHERE LEFT(NAME, 3)='ABC' 或者WHERE SUBSTRING(NAME,1, 3)='ABC'
应改?
WHERE NAME LIKE 'ABC%'
日期查询的例子:
WHERE DATEDIFF(DAY, 日期,'2005-11-30')=0应改?WHERE 日期 >='2005-11-30' AND 日期 <'2005-12-1‘
WHERE DATEDIFF(DAY, 日期,'2005-11-30')>0应改?WHERE 日期 <'2005-11-30‘
WHERE DATEDIFF(DAY, 日期,'2005-11-30')>=0应改?WHERE 日期 <'2005-12-01‘
WHERE DATEDIFF(DAY, 日期,'2005-11-30')<0应改?WHERE 日期>='2005-12-01‘
WHERE DATEDIFF(DAY, 日期,'2005-11-30')<=0应改?WHERE 日期>='2005-11-30‘
D、不要对索引字段q行多字D连?/p>
比如Q?/p>
WHERE FAME+ ’.’+LNAME=‘HAIWEI.YANG’
应改?
WHERE FNAME=‘HAIWEI’ AND LNAME=‘YANG’
8?注意q接条g的写?/p>
多表q接的连接条件对索引的选择有着重要的意义,所以我们在写连接条件条件的时候需要特别的注意?/p>
A、多表连接的时候,q接条g必须写全Q宁可重复,不要~漏?/p>
B?q接条g量使用聚集索引
C?注意ON部分条g和WHERE部分条g的区?/p>
9?其他需要注意的地方
l验表明Q问题发现的早解决的成本越低,很多性能问题可以在编码阶D就发现Qؓ了提早发现性能问题Q需要注意:
A、程序员注意、关心各表的数据量?/p>
B?~码q程和单元测试过E尽量用数据量较大的数据库测试,最好能用实际数据测试?/p>
C?每个SQL语句量?/p>
D、不要频J更新有触发器的表的数据
E?注意数据库函数的限制以及其性能
10?nbsp; 学会分辩SQL语句的优?/p>
自己分LSQL语句的优劣非帔R要,只有自己能分辨优劣才能写出高效的语句?/p>
A?nbsp; 查看SQL语句的执行计划,可以在查询分析其使用CTRL+L囑Ş化的昄执行计划Q一般应该注意百分比最大的几个囑Ş的属性,把鼠标移动到其上面会昄q个囑Ş的属性,需要注意预计成本的数据Q也要注意其标题Q一般都是CLUSTERED INDEX SEEK 、INDEX SEEK 、CLUSTERED INDEX SCAN 、INDEX SCAN 、TABLE SCAN{,其中出现SCAN说明语句有优化的余地。也可以用语?/p>
SET SHOWPLAN_ALL ON
要执行的语句
SET SHOWPLAN_ALL OFF
查看执行计划的文本详l信息?/p>
B?nbsp; 用事件探查器跟踪pȝ的运行,可疑跟踪到执行的语句Q以及所用的旉QCPU用量以及I/O数据Q从而分析语句的效率?/p>
C?nbsp; 可以用WINDOWS的系l性能器Q关注CPU、I/O参数
四?nbsp; 试、试q行、维护阶D?/p>
试的主要Q务是发现q修改系l的问题Q其中性能问题也是一个重要的斚w。重点应该放在发现有性能问题的地方,q进行必要的优化。主要进行语句优化、烦引优化等?/p>
试运行和l护阶段是在实际的环境下q行pȝQ发现的问题范围更广Q可能涉及操作系l、网l以及多用户q发环境出现的问题,其优化也扩展到操作系l、网l以及数据库物理存储的优化?/p>
q个阶段的优花方法在q里不再展开Q只说明下烦引维护的ҎQ?/p>
A?nbsp; 可以用DBCC DBREINDEX语句或者SQL SERVERl护计划讑֮定时q行索引重徏Q烦引重建的目的是提高烦引的效能?/p>
B?nbsp; 可以用语句UPDATE STATISTICS或者SQL SERVERl护计划讑֮定时q行索引l计信息的更斎ͼ其目的是使得l计信息更能反映实际情况Q从而得优化器选择更合适的索引?/p>
C?nbsp; 可以用DBCC CHECKDB或者DBCC CHECKTABLE语句查数据库表和索引是否有问题,q两个语句也能修复一般的问题?/p>
D?nbsp;
五?nbsp; |上资料中一些说法的个h不同意见
1?“应尽量避免在 WHERE 子句中对字段q行 NULL 值判断,否则导致引擎放弃用烦引而进行全表扫描,如:
SELECT ID FROM T WHERE NUM IS NULL
可以在NUM上设|默认?Q确保表中NUM列没有NULL|然后q样查询Q?/p>
SELECT ID FROM T WHERE NUM=0”
个h意见Q经q测试,IS NULL也是可以用INDEX SEEK查找的,0和NULL是不同概늚Q以上说法的两个查询的意义和记录数是不同的?/p>
2?“应尽量避免在 WHERE 子句中?=?lt;>操作W,否则引擎放弃用烦引而进行全表扫描?#8221;
个h意见Q经q测试,<>也是可以用INDEX SEEK查找的?/p>
3?“应尽量避免在 WHERE 子句中?OR 来连接条Ӟ否则导致引擎放弃用烦引而进行全表扫描,如:
SELECT ID FROM T WHERE NUM=10 OR NUM=20
可以q样查询Q?/p>
SELECT ID FROM T WHERE NUM=10
UNION ALL
SELECT ID FROM T WHERE NUM=20”
个h意见Q主要对全表扫描的说法不赞同?/p>
4?“IN ?NOT IN 也要慎用Q否则会D全表扫描Q如Q?/p>
SELECT ID FROM T WHERE NUM IN(1,2,3)
对于q箋的数|能用 BETWEEN ׃要用 IN 了:
SELECT ID FROM T WHERE NUM BETWEEN 1 AND 3”
个h意见Q主要对全表扫描的说法不赞同?/p>
5?“如果?WHERE 子句中用参敎ͼ也会D全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将讉K计划的选择推迟到运行时Q它必须在编译时q行选择。然而,如果在编译时建立讉K计划Q变量的D是未知的Q因而无法作为烦引选择的输入项。如下面语句进行全表扫描:
SELECT ID FROM T WHERE NUM=@NUM
可以改ؓ强制查询使用索引Q?/p>
SELECT ID FROM T WITH(INDEX(索引?) WHERE NUM=@NUM”
个h意见Q关于局部变量的解释比较奇怪,使用参数如果会媄响性能Q那存储q程p校除了,我坚持我上面对于强制索引的看法?/p>
6?“可能的使用 VARCHAR/NVARCHAR 代替 CHAR/NCHAR Q因为首先变长字D存储空间小Q可以节省存储空_其次对于查询来说Q在一个相对较的字段内搜索效率显然要高些?#8221;
个h意见Q?#8220;在一个相对较的字段内搜索效率显然要高些”昄是对的,但是字段的长短似乎不是由变不变长军_Q而是业务本n军_。在SQLSERVER6.5或者之前版本,不定长字W串字段的比较速度比定长的字符串字D늚比较速度慢很多,所以对于那些版本,我们都是推荐使用定长字段存储一些关键字Dc而在2000版本Q修改了不定长字W串字段的比较方法,与定长字D늚比较速度差别不大了,q样Z方便Q我们大量用不定长字段?/p>
7?关于q接表的序或者条件的序的说法,l过试Q在SQL SERVERQ这些顺序都是不影响性能的,q些说法可能是对ORACLE有效?/p>
MS SQL Server查询优化Ҏ
查询速度慢的原因很多Q常见如下几U?nbsp;
1、没有烦引或者没有用到烦?q是查询慢最常见的问题,是程序设计的~陷)
2、I/O吞吐量小QŞ成了瓉效应?nbsp;
3、没有创列D查询不优化?nbsp;
4、内存不?nbsp;
5、网l速度?nbsp;
6、查询出的数据量q大Q可以采用多ơ查询,其他的方法降低数据量Q?nbsp;
7、锁或者死?q也是查询慢最常见的问题,是程序设计的~陷)
8、sp_lock,sp_who,zd的用h?原因是读写竞争资源?nbsp;
9、返回了不必要的行和?nbsp;
10、查询语句不好,没有优化
可以通过如下Ҏ来优化查?nbsp;
1、把数据、日志、烦引放C同的I/O讑֤上,增加d速度Q以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量Q尺寸)大Q提高I/O重?
2、纵向、横向分割表Q减表的尺?sp_spaceuse)
3、升U硬?nbsp;
4、根据查询条?建立索引,优化索引、优化访问方式,限制l果集的数据量。注意填充因子要适当Q最好是使用默认?Q。烦引应该尽量小Q用字节数的列徏索引好(参照索引的创建),不要Ҏ限的几个值的字段建单一索引如性别字段
5、提高网?
6、扩大服务器的内?Windows 2000和SQL server 2000能支?-8G的内存。配|虚拟内存:虚拟内存大小应基于计机上ƈ发运行的服务q行配置。运?nbsp; Microsoft SQL Server? 2000 Ӟ可考虑虚拟内存大设|ؓ计算Z安装的物理内存的 1.5 倍。如果另外安装了全文索功能,q打运?nbsp; Microsoft 搜烦服务以便执行全文索引和查询,可考虑Q将虚拟内存大小配置是计算Z安装的物理内存的 3 倍。将 SQL Server max server memory 服务器配|选项配置为物理内存的 1.5 倍(虚拟内存大小讄的一半)?nbsp;
7、增加服务器CPU个数;但是必须明白q行处理串行处理更需要资源例如内存。用ƈ行还是串行程是MsSQL自动评估选择的。单个Q务分解成多个dQ就可以在处理器上运行。例如耽搁查询的排序、连接、扫描和GROUP BY字句同时执行QSQL SERVERҎpȝ的负载情况决定最优的q行{Q复杂的需要消耗大量的CPU的查询最适合q行处理。但是更新操作UPDATE,INSERTQ?DELETEq不能ƈ行处理?nbsp;
8、如果是使用likeq行查询的话Q简单的使用index是不行的Q但是全文烦引,耗空间?nbsp; like 'a%' 使用索引 like '%a' 不用烦引用 like '%a%' 查询Ӟ查询耗时和字D值总长度成正比,所以不能用CHARcdQ而是VARCHAR。对于字D늚值很长的建全文烦引?nbsp;
9、DB Server 和APPLication Server 分离QOLTP和OLAP分离
10、分布式分区视图可用于实现数据库服务器联合体。联合体是一l分开理的服务器Q但它们怺协作分担pȝ的处理负荗这U通过分区数据形成数据库服务器联合体的机制能够扩大一l服务器Q以支持大型的多?nbsp; Web 站点的处理需要。有x多信息,参见设计联合数据库服务器。(参照SQL帮助文g'分区视图'Q?nbsp;
a、在实现分区视图之前Q必d水^分区?nbsp;
b、在创徏成员表后Q在每个成员服务器上定义一个分布式分区视图Qƈ且每个视囑օ有相同的名称。这P引用分布式分囑的查询可以在M一个成员服务器上运行。系l操作如同每个成员服务器上都有一个原始表的复本一P但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的?nbsp;
11、重建烦?nbsp; DBCC REINDEX ,DBCC INDEXDEFRAG,收羃数据和日?nbsp; DBCC SHRINKDB,DBCC SHRINKFILE. 讄自动收羃日志.对于大的数据库不要设|数据库自动增长Q它会降低服务器的性能?nbsp; 在T-sql的写法上有很大的讲究Q下面列出常见的要点Q首先,DBMS处理查询计划的过E是q样的:
(1)?nbsp; 查询语句的词法、语法检?nbsp;
(2)?nbsp; 语句提交给DBMS的查询优化器
(3)?nbsp; 优化器做代数优化和存取\径的优化
(4)?nbsp; 由预~译模块生成查询规划
(5)?nbsp; 然后在合适的旉提交l系l处理执?nbsp;
(6)?nbsp; 最后将执行l果q回l用户其ơ,看一下SQL SERVER的数据存攄l构Q一个页面的大小?K(8060)字节Q?个页面ؓ一个盘区,按照B树存放?nbsp;
12、Commit和rollback的区?nbsp; Rollback:回滚所有的事物?nbsp; Commit:提交当前的事? 没有必要在动态SQL里写事物Q如果要写请写在外面如: begin tran exec(@s) commit trans 或者将动态SQL 写成函数或者存储过E?nbsp;
13、在查询Select语句中用Where字句限制q回的行?避免表扫?如果q回不必要的数据Q浪费了服务器的I/O资源Q加重了|络的负担降低性能。如果表很大Q在表扫描的期间表锁住Q禁止其他的联接讉K?后果严重?nbsp;
14、SQL的注释申明对执行没有M影响
15、尽可能不用光标,它占用大量的资源。如果需要row-by-row地执行,量采用非光标技?如:在客L循环Q用临时表,Table变量Q用子查询,用Case语句{等。游标可以按照它所支持的提取选项q行分类Q?nbsp; 只进 必须按照从第一行到最后一行的序提取行?/span>FETCH NEXT 是唯一允许的提取操?也是默认方式。可滚动?nbsp; 可以在游标中M地方随机提取L行。游标的技术在SQL2000下变得功能很强大Q他的目的是支持循环?nbsp;
有四个ƈ发选项
READ_ONLYQ不允许通过游标定位更新(Update)Q且在组成结果集的行中没有锁?nbsp;
OPTIMISTIC WITH valueS:乐观q发控制是事务控制理论的一个标准部分。乐观ƈ发控制用于这L情ŞQ即在打开游标及更新行的间隔中Q只有很的Z让第二个用户更新某一行。当某个游标以此选项打开Ӟ没有锁控制其中的行,q将有助于最大化其处理能力。如果用戯图修Ҏ一行,则此行的当前g与最后一ơ提取此行时获取的D行比较。如果Q何值发生改变,则服务器׃知道其他人已更新了此行,q会q回一个错误。如果值是一LQ服务器执行修攏V?nbsp; 选择q个q发选项OPTIMISTIC WITH ROW VERSIONING:此乐观ƈ发控刉项Z行版本控制。用行版本控制Q其中的表必d有某U版本标识符Q服务器可用它来定该行在读入游标后是否有所更改?nbsp;
?nbsp; SQL Server 中,q个性能?nbsp; timestamp 数据cd提供Q它是一个二q制数字Q表C数据库中更改的相对序。每个数据库都有一个全局当前旉戛_|@@DBTS。每ơ以M方式更改带有 timestamp 列的行时QSQL Server 先在旉戛_中存储当前的 @@DBTS |然后增加 @@DBTS 的倹{如果某 个表h timestamp 列,则时间戳会被记到行。服务器可以比较某行的当前旉戛_和上次提取时所存储的时间戳|从而确定该行是否已更新。服务器不必比较所有列的|只需比较 timestamp 列即可。如果应用程序对没有 timestamp 列的表要求基于行版本控制的乐观ƈ发,则游标默认ؓZ数值的乐观q发控制?nbsp;
SCROLL LOCKS q个选项实现悲观q发控制。在悲观q发控制中,在把数据库的行读入游标结果集Ӟ应用E序试N定数据库行。在使用服务器游标时Q将行读入游标时会在其上攄一个更新锁。如果在事务内打开游标Q则该事务更新锁一直保持到事务被提交或回滚Q当提取下一行时Q将除去游标锁。如果在事务外打开游标Q则提取下一行时Q锁p丢弃。因此,每当用户需要完全的悲观q发控制Ӟ游标都应在事务内打开。更新锁阻止Q何其它Q务获取更新锁或排它锁Q从而阻止其它Q务更新该行?nbsp;
然而,更新锁ƈ不阻止共享锁Q所以它不会L其它dd行,除非W二个Q务也在要求带更新锁的d。滚动锁Ҏ在游标定义的 SELECT 语句中指定的锁提C,q些游标q发选项可以生成滚动锁。滚动锁在提取时在每行上获取Qƈ保持Cơ提取或者游标关闭,以先发生者ؓ准。下ơ提取时Q服务器为新提取中的行获取滚动锁Qƈ释放上次提取中行的滚动锁。滚动锁独立于事务锁Qƈ可以保持C个提交或回滚操作之后。如果提交时关闭游标的选项为关Q则 COMMIT 语句q不关闭M打开的游标,而且滚动锁被保留到提交之后,以维护对所提取数据的隔R所获取滚动锁的cd取决于游标ƈ发选项和游?nbsp; SELECT 语句中的锁提C?nbsp;
锁提C?nbsp; 只读 乐观数?nbsp; 乐观行版本控?nbsp; 锁定无提C?nbsp; 未锁?nbsp; 未锁?nbsp; 未锁?nbsp; 更新 NOLOCK 未锁?nbsp; 未锁?nbsp; 未锁?nbsp; 未锁?nbsp; HOLDLOCK ׃n ׃n ׃n 更新 UPDLOCK 错误 更新 更新 更新 TABLOCKX 错误 未锁?nbsp; 未锁?nbsp; 更新其它 未锁?nbsp; 未锁?nbsp; 未锁?nbsp; 更新 *指定 NOLOCK 提示指定了该提示的表在游标内是只ȝ?nbsp;
16、用Profiler来跟t查询,得到查询所需的时_扑ևSQL的问题所?用烦引优化器优化索引
17、注意UNion和UNion all 的区别?/span>UNION all?nbsp;
18、注意用DISTINCTQ在没有必要时不要用Q它同UNION一样会使查询变慢。重复的记录在查询里是没有问题的
19、查询时不要q回不需要的行、列
20、用sp_configure 'query governor cost limit'或者SET QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源出限制Ӟ服务器自动取消查?在查询之前就扼杀掉?SET LOCKTIME讄锁的旉
21、用select top 100 / 10 Percent 来限制用戯回的行数或者SET ROWCOUNT来限制操作的?nbsp;
22、在SQL2000以前Q一般不要用如下的字? "IS NULL", " <> ", "!=", "!> ", "! <", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%500'"Q因Z们不走烦引全是表扫描。也不要在WHere字句中的列名加函敎ͼ如ConvertQsubstring{?如果必须用函数的时候,创徏计算列再创徏索引来替?q可以变通写法:WHERE SUBSTRING(firstname,1,1) = 'm'改ؓWHERE firstname like 'm%'Q烦引扫描)Q一定要函数和列名分开。ƈ且烦引不能徏得太多和太大?/span>NOT IN会多ơ扫描表Q用EXISTS?/span>NOT EXISTS Q?/span>IN , LEFT OUTER JOIN 来替代,特别是左q接,而Exists比IN更快Q最慢的是NOT操作.如果列的值含有空Q以前它的烦引不起作用,现在2000的优化器能够处理了。相同的是IS NULLQ?#8220;NOT", "NOT EXISTS", "NOT IN"能优化她Q?#8221; <> ”{还是不能优化,用不到烦引?nbsp;
23、用Query AnalyzerQ查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20%的代码占据了80%的资源,我们优化的重Ҏq些慢的地方?nbsp;
24、如果用了IN或者OR{时发现查询没有走烦引,使用昄x指定索引Q?nbsp; SELECT * FROM PersonMember (INDEX = IX_Title) WHERE processid IN (‘?#8217;Q?#8216;?#8217;)
25、将需要查询的l果预先计算好放在表中,查询的时候再SELECT。这在SQL7.0以前是最重要的手Dc例如医院的住院费计?nbsp;
26?/span>MIN() ?nbsp; MAX()能用到合适的索引
27、数据库有一个原则是代码L据越q越好,所以优先选择Default,依次为Rules,Triggers, ConstraintQ约束如外健dCheckUNIQUE……,数据cd的最大长度等{都是约束),Procedure.q样不仅l护工作,~写E序质量高,q且执行的速度快?nbsp;
28、如果要插入大的二进制值到Image列,使用存储q程Q千万不要用内嵌INsert来插?不知JAVA是否)。因样应用程序首先将二进制D{换成字符Ԍ寸是它的两倍)Q服务器受到字符后又他转换成二q制?存储q程没有这些动? ҎQ?/span>Create procedure p_insert as insert into table(Fimage) values (@image), 在前台调用这个存储过E传入二q制参数Q这样处理速度明显改善?nbsp;
29、Between在某些时候比IN速度更快,Between能够更快地根据烦引找到范围。用查询优化器可见到差别?nbsp; select * from chineseresume where title in ('?/span>','?/span>') Select * from chineseresume where between '?/span>' and '?/span>' 是一L。由于in会在比较多次Q所以有时会慢些?nbsp;
30、在必要是对全局或者局部时表创徏索引Q有时能够提高速度Q但不是一定会q样Q因为烦引也耗费大量的资源。他的创建同是实际表一栗?nbsp;
31、不要徏没有作用的事物例如生报表时Q浪费资源。只有在必要使用事物时用它?nbsp;
32、用OR的字句可以分解成多个查询Qƈ且通过UNION q接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合烦引,用UNION all执行的效率更?多个OR的字句没有用到烦引,改写成UNION的Ş式再试图与烦引匹配。一个关键的问题是否用到索引?nbsp;
33、尽量少用视图,它的效率低。对视图操作比直接对表操作慢,可以用stored procedure来代替她。特别的是不要用视图嵌套,嵌套视图增加了寻扑֎始资料的隑ֺ。我们看视图的本质:它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时Q不要用指向多个表的视图,直接从表索或者仅仅包含这个表的视图上读,否则增加了不必要的开销,查询受到q扰.Z加快视图的查询,MsSQL增加了视囄引的功能?nbsp;
34、没有必要时不要用DISTINCT和ORDER BYQ这些动作可以改在客L执行。它们增加了额外的开销。这同UNION 和UNION ALL一L道理?nbsp; SELECT top 20 ad.companyname,comid,position,ad.referenceid,worklocation, convert(varchar(10),ad.postDate,120) as postDate1,workyear,degreedescription FROM jobcn_query.dbo.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
35、在IN后面值的列表中,出现最频繁的值放在最前面Q出现得最的攑֜最后面Q减判断的ơ数
36、当用SELECT INTOӞ它会锁住pȝ?sysobjectsQsysindexes{等)Q阻塞其他的q接的存取。创Z时表时用昄x语句Q而不?select INTO. drop table t_lxh begin tran select * into t_lxh from chineseresume where name = 'XYZ' --commit 在另一个连接中SELECT * from sysobjects可以看到 SELECT INTO 会锁住系l表QCreate table 也会锁系l表(不管是时表q是pȝ?。所以千万不要在事物内用它Q!Q这L话如果是l常要用的时表请用实表,或者时表变量?nbsp;
37、一般在GROUP BY 个HAVING字句之前p剔除多余的行Q所以尽量不要用它们来做剔除行的工作。他们的执行序应该如下最优:select 的Where字句选择所有合适的行,Group By用来分组个统计行QHaving字句用来剔除多余的分l。这样Group By 个Having的开销,查询?对于大的数据行进行分l和Having十分消耗资源。如果Group BY的目的不包括计算Q只是分l,那么用Distinct更快
38、一ơ更新多条记录比分多ơ更新每ơ一条快,是说批处理?nbsp;
39、少用时表Q尽量用l果集和TablecL的变量来代替它,Table cd的变量比临时表好
40、在SQL2000下,计算字段是可以烦引的Q需要满的条g如下Q?nbsp;
a、计字D늚表达是确定的
b、不能用在TEXT,NtextQImage数据cd
c、必配制如下选项 ANSI_NULLS = ON, ANSI_PADDINGS = ON, …….
41、尽量将数据的处理工作放在服务器上,减少|络的开销Q如使用存储q程。存储过E是~译好、优化过、ƈ且被l织C个执行规划里、且存储在数据库中的 SQL语句Q是控制语a的集合,速度当然快。反复执行的动态SQL,可以使用临时存储q程Q该q程Q时表Q被攑֜Tempdb中。以前由于SQL SERVER对复杂的数学计算不支持,所以不得不这个工作放在其他的层上而增加网l的开销。SQL2000支持UDFs,现在支持复杂的数学计,函数的返回g要太大,q样的开销很大。用戯定义函数象光标一h行的消耗大量的资源Q如果返回大的结果采用存储过E?nbsp;
42、不要在一句话里再三的使用相同的函敎ͼ费资源,结果放在变量里再调用更?nbsp;
43?/span>SELECT COUNT(*)的效率教低,量变通他的写法,而EXISTS?同时h意区别: select count(Field of null) from Table ?nbsp; select count(Field of NOT null) from Table 的返回值是不同的?nbsp;
44、当服务器的内存够多Ӟ配制U程数量 = 最大连接数+5Q这栯发挥最大的效率Q否则?nbsp; 配制U程数量 <最大连接数启用SQL SERVER的线E池来解?如果q是数量 = 最大连接数+5Q严重的损害服务器的性能?nbsp;
45、按照一定的ơ序来访问你的表。如果你先锁住表AQ再锁住表BQ那么在所有的存储q程中都要按照这个顺序来锁定它们。如果你Q不l意的)某个存储q程中先锁定表BQ再锁定表AQ这可能׃D一个死锁。如果锁定顺序没有被预先详细的设计好Q死锁很难被发现
46、通过SQL Server Performance Monitor监视相应g的负?nbsp; Memory: Page Faults / sec计数器如果该值偶走高,表明当时有线E竞争内存。如果持l很高,则内存可能是瓉?nbsp; Process:
(1)?/span>% DPC Time 指在范例间隔期间处理器用在缓延程序调?DPC)接收和提供服务的癑ֈ比?DPC 正在q行的ؓ比标准间隔优先权低的间隔)?nbsp; ׃ DPC 是以Ҏ模式执行的,DPC 旉的百分比为特权时?nbsp; 癑ֈ比的一部分。这些时间单独计ƈ且不属于间隔计算L的一?nbsp; 分。这个L昄了作为实例时间百分比的^均忙时?nbsp;
(2)?/span>%Processor Time计数器 如果该参数值持l超q?5%Q表明瓶颈是CPU。可以考虑增加一个处理器或换一个更快的处理器?nbsp;
(3)?/span>% Privileged Time 指非闲置处理器时间用于特权模式的癑ֈ比?Ҏ模式是ؓ操作pȝlg和操U늡仉动程序而设计的一U处理模式。它允许直接讉Kg和所有内存。另一U模式ؓ用户模式Q它是一Uؓ应用E序、环境分pȝ和整数分pȝ设计的一U有限处理模式。操作系l将应用E序U程转换成特权模式以讉K操作pȝ服务)?nbsp; Ҏ旉?nbsp; % 包括为间断和 DPC 提供服务的时间。特权时间比率高可能是由于失败设备生的大数量的间隔而引L。这个计数器^均忙时作为样本时间的一部分昄?nbsp;
(4)?/span>% User Time表示耗费CPU的数据库操作Q如排序Q执行aggregate functions{。如果该值很高,可考虑增加索引Q尽量用简单的表联接,水^分割大表格等Ҏ来降低该倹{?nbsp; Physical Disk: Curretn Disk Queue Length计数器该值应不超q磁盘数?.5~2倍。要提高性能Q可增加盘?nbsp; SQLServer:Cache Hit Ratio计数器该D高越好。如果持l低?0%Q应考虑增加内存?nbsp; 注意该参数值是从SQL Server启动后,׃直篏加记敎ͼ所以运行经q一D|间后Q该值将不能反映pȝ当前倹{?nbsp;
47、分析select emp_name form employee where salary > 3000 在此语句中若salary是Floatcd的,则优化器对其q行优化为Convert(float,3000)Q因?000是个整数Q我们应在编E时使用3000.0而不要等q行时让DBMSq行转化。同样字W和整型数据的{换?/span>