﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-AndyZhang-随笔分类-sql优化</title><link>http://www.blogjava.net/AndyZhang/category/49413.html</link><description>welcome to java world</description><language>zh-cn</language><lastBuildDate>Fri, 19 Aug 2011 09:38:27 GMT</lastBuildDate><pubDate>Fri, 19 Aug 2011 09:38:27 GMT</pubDate><ttl>60</ttl><item><title>关于SQL SERVER高并发访问的解决办法 </title><link>http://www.blogjava.net/AndyZhang/archive/2011/08/19/356877.html</link><dc:creator>SkyDream</dc:creator><author>SkyDream</author><pubDate>Fri, 19 Aug 2011 08:58:00 GMT</pubDate><guid>http://www.blogjava.net/AndyZhang/archive/2011/08/19/356877.html</guid><description><![CDATA[<p><span style="color: #000000">关于SQL SERVER高并发访问的解决办法<br />问题:<br />后台系统需要连接SQL SERVER，瞬时数据操作可能会很大，如同时有好几万数据要插入数据库，但在插入数据的同时另外的线程可能还要访问本数据库，插入数据的操作优先级比较低，其他的访问需要即时返回，想了很久不知道该怎么解决，望高手赐教了。<br />回答:<br /></span><span style="font-weight: bold; color: #800000">1</span><span style="color: #000000">. 提高服务器硬件配置 <br /></span><span style="font-weight: bold; color: #800000">2</span><span style="color: #000000">. 使用 </span><span style="color: #0000ff">replication</span><span style="color: #000000"> 之类的同步技术, 将频繁操作的表同步为多份, 将操作分散到这些同步的表中 <br /></span><span style="font-weight: bold; color: #800000">3</span><span style="color: #000000">. 对于数据查询, 尽量使用 </span><span style="color: #0000ff">READ</span><span style="color: #000000"> </span><span style="color: #0000ff">UNCOMMITTED</span><span style="color: #000000"> 事务隔离级别, 以减少锁的开销<br /><br /></span><span style="color: #808080">===</span><span style="color: #000000"><br /><br />可以使用快照隔离级别<br /><br /></span><span style="color: #808080">===</span><span style="color: #000000"><br /><br /></span><span style="color: #008080">--</span><span style="color: #008080">查询时，使用</span><span style="color: #008080"><br /></span><span style="color: #0000ff">SET</span><span style="color: #000000"> </span><span style="color: #0000ff">TRANSACTION</span><span style="color: #000000"> </span><span style="color: #0000ff">ISOLATION</span><span style="color: #000000"> </span><span style="color: #0000ff">LEVEL</span><span style="color: #000000"> </span><span style="color: #0000ff">READ</span><span style="color: #000000"> </span><span style="color: #0000ff">UNCOMMITTED</span><span style="color: #000000"><br /></span><span style="color: #008080">--</span><span style="color: #008080">或</span><span style="color: #008080"><br /></span><span style="color: #0000ff">with</span><span style="color: #000000">(nolock)<br /></span><span style="color: #008080">--</span><span style="color: #008080">并且建好索引，尽量减少耗时查询</span><span style="color: #008080"><br /></span><span style="color: #000000"><br /></span><span style="color: #008080">--</span><span style="color: #008080">多个服务器分担压力</span><span style="color: #008080"><br />--</span><span style="color: #008080">提高硬件性能这是最耗成本但最有效的方法了</span><span style="color: #008080"><br /></span><span style="color: #000000"><br /></span><span style="color: #808080">===</span><span style="color: #000000"><br /><br />查询时候不上锁 <br /><br /></span><span style="color: #0000ff">select</span><span style="color: #000000"> </span><span style="color: #808080">*</span><span style="color: #0000ff">from</span><span style="color: #000000"> tb(nolock) <br /><br />使用镜像复制等 添加一些只读的数据库！<br /><br /></span><span style="color: #808080">===</span><span style="color: #000000"><br /><br />除了以上的方法之外 <br />2005还可以采用row</span><span style="color: #808080">-</span><span style="color: #000000">versiong based的snapshot 事务隔离层级。 <br />建立snapshot </span><span style="color: #0000ff">database</span><span style="color: #000000">，将查询、更新两种不同操作隔离<br /></span></p><img src ="http://www.blogjava.net/AndyZhang/aggbug/356877.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AndyZhang/" target="_blank">SkyDream</a> 2011-08-19 16:58 <a href="http://www.blogjava.net/AndyZhang/archive/2011/08/19/356877.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MSSQL优化之————探索MSSQL执行计划 </title><link>http://www.blogjava.net/AndyZhang/archive/2011/08/19/356876.html</link><dc:creator>SkyDream</dc:creator><author>SkyDream</author><pubDate>Fri, 19 Aug 2011 08:57:00 GMT</pubDate><guid>http://www.blogjava.net/AndyZhang/archive/2011/08/19/356876.html</guid><description><![CDATA[<p>MSSQL优化之&#8212;&#8212;&#8212;&#8212;探索MSSQL执行计划</p>
<p>&nbsp;</p>
<p>作者：no_mIss&nbsp; QQ:34813284</p>
<p>时间：2006.11.07 23:30:00</p>
<p>环境：win2003 + mssql2005 </p>
<p>&nbsp;</p>
<p>最近总想整理下对MSSQL的一些理解与感悟，却一直没有心思和时间写，晚上无事便写了一篇探索MSSQL执行计划,本文讲执行计划但不仅限于讲执行计划。 </p>
<p>网上的SQL优化的文章实在是很多，说实在的，我也曾经到处找这样的文章，什么不要使用IN了，什么OR了，什么AND了，很多很多，还有很多人拿出仅几S甚至几MS的时间差的例子来证明着什么(有点可笑)，让许多人不知道其是对还是错。而SQL优化又是每个要与数据库打交道的程序员的必修课，所以写了此文，与朋友们共勉。 </p>
<p>谈到优化就必然要涉及索引，就像要讲锁必然要说事务一样，所以你需要了解一下索引，仅仅是索引，就能讲半天了，所以索引我就不说了(打很多字是很累的，况且我也知之甚少)，可以去参考相关的文章，这个网上资料比较多了。 </p>
<p>今天来探索下MSSQL的执行计划，来让大家知道如何查看MSSQL的优化机制，以此来优化SQL查询。</p>
<p>&nbsp;</p>
<p>--DROP TABLE T_UserInfo----------------------------------------------------</p>
<p>--建测试表</p>
<p>CREATE TABLE T_UserInfo</p>
<p>(</p>
<p>&nbsp;&nbsp;&nbsp; Userid varchar(20),&nbsp; UserName varchar(20),</p>
<p>&nbsp;&nbsp;&nbsp; RegTime datetime, Tel varchar(20),</p>
<p>)</p>
<p>--插入测试数据</p>
<p>DECLARE @I INT</p>
<p>DECLARE @ENDID INT</p>
<p>SELECT @I = 1</p>
<p>SELECT @ENDID = 100&nbsp; --在此处更改要插入的数据，重新插入之前要删掉所有数据</p>
<p>WHILE @I &lt;= @ENDID</p>
<p>BEGIN</p>
<p>&nbsp;&nbsp;&nbsp; INSERT INTO T_UserInfo </p>
<p>&nbsp;&nbsp;&nbsp; SELECT 'ABCDE'+CAST(@I AS VARCHAR(20))+'EF','李'+CAST(@I AS VARCHAR(20)),</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GETDATE(),'876543'+CAST(@I AS VARCHAR(20))</p>
<p>&nbsp;&nbsp;&nbsp; SELECT @I = @I + 1</p>
<p>END</p>
<p>&nbsp;</p>
<p>--相关SQL语句解释</p>
<p>---------------------------------------------------------------------------</p>
<p>--建聚集索引</p>
<p>CREATE CLUSTERED INDEX INDEX_Userid&nbsp; ON T_UserInfo (Userid)</p>
<p>--建非聚集索引</p>
<p>CREATE NONCLUSTERED INDEX INDEX_Userid&nbsp; ON T_UserInfo (Userid)</p>
<p>--删除索引</p>
<p>DROP INDEX T_UserInfo.INDEX_Userid</p>
<p>---------------------------------------------------------------------------</p>
<p>---------------------------------------------------------------------------</p>
<p>--显示有关由Transact-SQL 语句生成的磁盘活动量的信息</p>
<p>SET STATISTICS IO ON</p>
<p>--关闭有关由Transact-SQL 语句生成的磁盘活动量的信息</p>
<p>SET STATISTICS IO OFF</p>
<p>--显示[返回有关语句执行情况的详细信息，并估计语句对资源的需求]</p>
<p>SET SHOWPLAN_ALL&nbsp; ON </p>
<p>--关闭[返回有关语句执行情况的详细信息，并估计语句对资源的需求]</p>
<p>SET SHOWPLAN_ALL&nbsp; OFF</p>
<p>---------------------------------------------------------------------------</p>
<p>请记住：SET STATISTICS IO 和 SET SHOWPLAN_ALL 是互斥的。</p>
<p>&nbsp;</p>
<p>OK，现在开始：</p>
<p>首先，我们插入100条数据</p>
<p>然后我写了一个查询语句：</p>
<p>SELECT * FROM T_UserInfo WHERE USERID='ABCDE6EF'</p>
<p>选中以上语句，按Ctrl+L，如下图</p>
<p><img height="308" alt="" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35lIxG80ORaScn_fFDsCyMbFJRYQ63SE5Chc1J77gXgv_n_xsagXYGkJDlDm0tjLS5lG4uwW7uym1kMKehbwjol7Lu2-4bOodWU" width="439" /></p>
<p>这就是MSSQL的执行计划：表扫描：扫描表中的行</p>
<p>&nbsp;</p>
<p>然后我们来看该语句对IO的读写：</p>
<p>执行:SET STATISTICS IO ON</p>
<p>此时再执行该SQL:SELECT * FROM T_UserInfo WHERE USERID='ABCDE6EF'</p>
<p>切换到消失栏显示如下：</p>
<p>表'T_UserInfo'。扫描计数1，逻辑读1 次，物理读0 次，预读0 次。</p>
<p>解释下其意思：</p>
<p>四个值分别为：</p>
<p>&nbsp;&nbsp;&nbsp; 执行的扫描次数;</p>
<p>&nbsp;&nbsp;&nbsp; 从数据缓存读取的页数;</p>
<p>&nbsp;&nbsp;&nbsp; 从磁盘读取的页数;</p>
<p>&nbsp;&nbsp;&nbsp; 为进行查询而放入缓存的页数</p>
<p>重要：如果对于一个SQL查询有多种写法，那么这四个值中的逻辑读(logical reads)决定了哪个是最优化的。</p>
<p>&nbsp;</p>
<p>接下来我们为其建一个聚集索引</p>
<p>执行CREATE CLUSTERED INDEX INDEX_Userid&nbsp; ON T_UserInfo (Userid)</p>
<p>然后再执行SELECT * FROM T_UserInfo WHERE USERID='ABCDE6EF'</p>
<p>切换到消息栏如下显示：</p>
<p>表'T_UserInfo'。扫描计数1，逻辑读2 次，物理读0 次，预读0 次。</p>
<p>此时逻辑读由原来的1变成2，</p>
<p>说明我们又加了一个索引页，现在我们查询时，逻辑读就是要读两页(1索引页+1数据页)，此时的效率还不如不建索引。</p>
<p>&nbsp;</p>
<p>此时再选中查询语句，然后再Ctrl+L，如下图:</p>
<p>&nbsp;</p>
<p><img height="332" alt="" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35lIxG80ORaScgkkAGx3aEZttzKHsG4EkA0y6LMlc2MxpjIxbCsv0jJ-ZyOYS5aqDysL8f8CQEs7zbKXFRUlKbhUSWMvXArjFtfftuIN0qCqXw" width="513" /></p>
<p>聚集索引查找：扫描聚集索引中特定范围的行</p>
<p>说明，此时用了索引。</p>
<p>&nbsp;</p>
<p>OK,到这里你应该已经知道初步知道MSSQL查询计划和如何查看对IO的读取消耗了吧！</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>接下来我们继续：</p>
<p>&nbsp;</p>
<p>现在我再把测试数据改变成1000条</p>
<p>再执行SET STATISTICS IO ON,再执行</p>
<p>SELECT * FROM T_UserInfo WHERE USERID='ABCDE6EF'</p>
<p>在不加聚集索引的情况下：</p>
<p>表'T_UserInfo'。扫描计数1，逻辑读7 次，物理读0 次，预读0 次。</p>
<p>在加聚集索引的情况下：CREATE CLUSTERED INDEX INDEX_Userid&nbsp; ON T_UserInfo (Userid)</p>
<p>表'T_UserInfo'。扫描计数1，逻辑读2 次，物理读0 次，预读0 次。</p>
<p>(其实也就是说此时是读了一个索引页，一个数据页)</p>
<p>如此，在数据量稍大时，索引的查询优势就显示出来了。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>先小总结下：</p>
<p>当你构建SQL语句时，按Ctrl+L就可以看到语句是如何执行，是用索引扫描还是表扫描？</p>
<p>通过SET STATISTICS IO ON 来查看逻辑读，完成同一功能的不同SQL语句，逻辑读</p>
<p>越小查询速度越快(当然不要找那个只有几百条记录的例子来反我)。</p>
<p>&nbsp; </p>
<p>我们再继续深入：</p>
<p>OK，现在我们再来看一次，我们换个SQL语句，来看下MSSQL如何来执行的此SQL呢？</p>
<p>现在去掉索引：DROP INDEX T_UserInfo.INDEX_Userid</p>
<p>现在打开[显示语句执行情况的详细信息]：SET SHOWPLAN_ALL&nbsp; ON </p>
<p>然后再执行：SELECT * FROM T_UserInfo WHERE USERID LIKE 'ABCDE8%'</p>
<p>看结果栏：结果中有些具体参数，比如IO的消耗，CPU的消耗。</p>
<p>在这里我们只看StmtText:</p>
<p>SELECT * FROM T_UserInfo WHERE USERID LIKE 'ABCDE8%'</p>
<p>&nbsp; |--Table Scan(OBJECT:([student].[dbo].[T_UserInfo]), WHERE:(like([T_UserInfo].[Userid], 'ABCDE8%', NULL)))</p>
<p>Ctrl+L看下此时的图行执行计划： </p>
<p><a href="http://blog.csdn.net/no_mIss/archive/2006/11/09/1374978.aspx"></a>&nbsp;<img height="319" alt="" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35lIxG80ORaScvJrO3qwdOFLoyxaK30BmZ0sVSH3uiA_0snE73BTnerXzGvzjBF2xaPRF588TocQ3NCB8UWxonuG8Dzc_AMUhCM" width="455" /></p>
<p>我再加上索引：</p>
<p>先关闭：SET SHOWPLAN_ALL OFF</p>
<p>再执行：CREATE CLUSTERED INDEX INDEX_Userid&nbsp; ON T_UserInfo (Userid)</p>
<p>再开启：SET SHOWPLAN_ALL ON</p>
<p>再执行：SELECT * FROM T_UserInfo WHERE USERID LIKE 'ABCDE8%'</p>
<p>查看StmtText:</p>
<p>SELECT * FROM T_UserInfo WHERE USERID LIKE 'ABCDE8%'</p>
<p>&nbsp; |--Clustered Index Seek(OBJECT:([student].[dbo].[T_UserInfo].[INDEX_Userid]), SEEK:([T_UserInfo].[Userid] &gt;= 'ABCDE8' AND [T_UserInfo].[Userid] &lt; 'ABCDE9'),&nbsp; WHERE:(like([T_UserInfo].[Userid], 'ABCDE8%', NULL)) ORDERED FORWARD)Ctrl+L看下此时的图行执行计划：</p>
<p>Ctrl+L看下此时的图行执行计划：</p>
<p><img height="331" alt="" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35lIxG80ORaScjCLRm3WoTnrHEcEAnhLFVSveHGrK0RGzfT4ZPuCI1K7oCquSOPfJs9FxOrPWX0v5Ir--VZq62ecTBrmqaFm5wdil_hdALtmTw" width="498" /><a href="http://blog.csdn.net/no_mIss/archive/2006/11/09/1374978.aspx"></a></p>
<p><a href="http://blog.csdn.net/no_mIss/archive/2006/11/09/1374978.aspx"></a></p>
<p>&nbsp;</p>
<p>在有索引的情况下，我们再写一个SQL：</p>
<p>SET SHOWPLAN_ALL ON</p>
<p>SELECT * FROM T_UserInfo WHERE LEFT(USERID,4)='ABCDE8%'</p>
<p>查看StmtText:</p>
<p>SELECT * FROM T_UserInfo WHERE LEFT(USERID,4)='ABCDE8%'</p>
<p>&nbsp; |--Clustered Index Scan(OBJECT:([student].[dbo].[T_UserInfo].[INDEX_Userid]), WHERE:(substring([T_UserInfo].[Userid], 1, 4)='ABCDE8%')) </p>
<p>Ctrl+L看下此时的图行执行计划：</p>
<p><img height="332" alt="" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35lIxG80ORaScku9bP8hHfW5BI4L4EKAS0Bm7lG0Ykrpboa06XpqJOmoCwDeFOaq-vfCs6OhifhfH7Nq17Lj7JNG9fr0eGcXCjmPhF6BZ4usWA" width="503" /></p>
<p>我们再分别看一下三种情况下对IO的操作</p>
<p>分别如下：</p>
<p>第一种情况：表'T_UserInfo'。扫描计数1，逻辑读7 次，物理读0 次，预读0 次。</p>
<p>第二种情况：表'T_UserInfo'。扫描计数1，逻辑读3 次，物理读0 次，预读0 次。</p>
<p>第三种情况：表'T_UserInfo'。扫描计数1，逻辑读8 次，物理读0 次，预读0 次。</p>
<p>这说明:</p>
<p>第一次是表扫描，扫了7页，也就是全表扫描</p>
<p>第二次是索引扫描，扫了1页索引，2页数据页</p>
<p>第三次是索引扫描+表扫描，扫了1页索引，7页数据页</p>
<p>[图形界面也有对CPU和IO的消耗，也可以看出来哪个最优!] </p>
<p>&nbsp;</p>
<p>通过比较，嘿嘿，很容易的看出：第二种第三种写法在都有索引的情况下，like有效的使用索引，而left则不能，这样一个最简单的优化的例子就出来了，哈哈。</p>
<p>&nbsp;</p>
<p>&nbsp;如果以上你都明白了，那么你可能已经对SQL的优化有初步新的想法了，网上一堆堆的SQL优化的文章真的是那样吗？你自己试试就知道了，而不必盲目去记那些东西，自己试试，看看MSSQL到底是怎么来执行就明白了。</p>
<p>在我举的例子中，用的是聚集索引扫描，字段是字母加数字，大家可以试试看纯数字的、字母的、汉字的等等，了解下MMSQL会如何改变SQL语句来利用索引。然后再试试非聚集索引是什么情况？用不用索引和什么有关？子查询MSSQL是如何执行？IN用不用索引，LIKE用不用索引？函数用不用索引？OR、AND、UNION？子查询呢？在这里我不一一去试给大家看了，只要知道了如何去看MSSQL的执行计划（图形和文本)，很多事情就很明朗了。</p>
<p>&nbsp;</p>
<p>大总结：</p>
<p>实现同一查询功能的SQL写法可能会有多种，如果判断哪种最优化，如果仅仅是从时间上来测，会受很多外界因素的影响，而我们明白了MSSQL如何去执行，通过IO逻辑读、通过查看图示的查询计划、通过其优化后而执行的SQL语句，才是优化SQL的真正途径。</p>
<p>&nbsp;</p>
<p>另外提醒下：数据量的多少有时会影响MSSQL对同一种查询写法语句的执行计划，这一点在非聚集索引上特别明显，还有就是在多CPU与单CPU下，在多用户并发情况下，同一写法的查询语句执行计划会有所不同，这个就需要大家有机会去试验了(我也没有这方面的太多经验与大家分享)。</p><img src ="http://www.blogjava.net/AndyZhang/aggbug/356876.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AndyZhang/" target="_blank">SkyDream</a> 2011-08-19 16:57 <a href="http://www.blogjava.net/AndyZhang/archive/2011/08/19/356876.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL SERVER性能优化综述</title><link>http://www.blogjava.net/AndyZhang/archive/2011/08/19/356875.html</link><dc:creator>SkyDream</dc:creator><author>SkyDream</author><pubDate>Fri, 19 Aug 2011 08:56:00 GMT</pubDate><guid>http://www.blogjava.net/AndyZhang/archive/2011/08/19/356875.html</guid><description><![CDATA[<p>SQL SERVER性能优化综述</p>
<p>&nbsp;</p>
<p>--原著:Haiwer<br />&nbsp;</p>
<p>近期因工作需要，希望比较全面的总结下SQL SERVER数据库性能优化相关的注意事项，在网上搜索了一下,发现很多文章,有的都列出了上百条,但是仔细看发现，有很多似是而非或者过时(可能对SQL SERVER6.5以前的版本或者ORACLE是适用的)的信息，只好自己根据以前的经验和测试结果进行总结了。</p>
<p>我始终认为，一个系统的性能的提高，不单单是试运行或者维护阶段的性能调优的任务，也不单单是开发阶段的事情，而是在整个软件生命周期都需要注意，进行有效工作才能达到的。所以我希望按照软件生命周期的不同阶段来总结数据库性能优化相关的注意事项。</p>
<p>一、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 分析阶段</p>
<p>一般来说，在系统分析阶段往往有太多需要关注的地方，系统各种功能性、可用性、可靠性、安全性需求往往吸引了我们大部分的注意力，但是，我们必须注意，性能是很重要的非功能性需求，必须根据系统的特点确定其实时性需求、响应时间的需求、硬件的配置等。最好能有各种需求的量化的指标。</p>
<p>另一方面，在分析阶段应该根据各种需求区分出系统的类型，大的方面，区分是OLTP（联机事务处理系统）和OLAP（联机分析处理系统）。</p>
<p>二、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 设计阶段</p>
<p>设计阶段可以说是以后系统性能的关键阶段，在这个阶段，有一个关系到以后几乎所有性能调优的过程&#8212;数据库设计。</p>
<p>在数据库设计完成后，可以进行初步的索引设计，好的索引设计可以指导编码阶段写出高效率的代码，为整个系统的性能打下良好的基础。</p>
<p>以下是性能要求设计阶段需要注意的：</p>
<p>1、 数据库逻辑设计的规范化</p>
<p>数据库逻辑设计的规范化就是我们一般所说的范式，我们可以这样来简单理解范式：</p>
<p>第1规范：没有重复的组或多值的列，这是数据库设计的最低要求。 </p>
<p>第2规范: 每个非关键字段必须依赖于主关键字，不能依赖于一个组合式主关键字的某些组成部分。消除部分依赖，大部分情况下，数据库设计都应该达到第二范式。</p>
<p>第3规范: 一个非关键字段不能依赖于另一个非关键字段。消除传递依赖，达到第三范式应该是系统中大部分表的要求，除非一些特殊作用的表。</p>
<p>更高的范式要求这里就不再作介绍了，个人认为，如果全部达到第二范式，大部分达到第三范式，系统会产生较少的列和较多的表，因而减少了数据冗余，也利于性能的提高。</p>
<p>2、 合理的冗余</p>
<p>完全按照规范化设计的系统几乎是不可能的，除非系统特别的小，在规范化设计后，有计划地加入冗余是必要的。</p>
<p>冗余可以是冗余数据库、冗余表或者冗余字段，不同粒度的冗余可以起到不同的作用。</p>
<p>冗余可以是为了编程方便而增加，也可以是为了性能的提高而增加。从性能角度来说，冗余数据库可以分散数据库压力，冗余表可以分散数据量大的表的并发压力，也可以加快特殊查询的速度，冗余字段可以有效减少数据库表的连接，提高效率。</p>
<p>3、 主键的设计</p>
<p>主键是必要的，SQL SERVER的主键同时是一个唯一索引，而且在实际应用中，我们往往选择最小的键组合作为主键，所以主键往往适合作为表的聚集索引。聚集索引对查询的影响是比较大的，这个在下面索引的叙述。</p>
<p>在有多个键的表，主键的选择也比较重要，一般选择总的长度小的键，小的键的比较速度快，同时小的键可以使主键的B树结构的层次更少。</p>
<p>主键的选择还要注意组合主键的字段次序，对于组合主键来说，不同的字段次序的主键的性能差别可能会很大，一般应该选择重复率低、单独或者组合查询可能性大的字段放在前面。</p>
<p>4、 外键的设计</p>
<p>外键作为数据库对象，很多人认为麻烦而不用，实际上，外键在大部分情况下是很有用的，理由是：</p>
<p>外键是最高效的一致性维护方法，数据库的一致性要求，依次可以用外键、CHECK约束、规则约束、触发器、客户端程序，一般认为，离数据越近的方法效率越高。</p>
<p>谨慎使用级联删除和级联更新，级联删除和级联更新作为SQL SERVER 2000当年的新功能，在2005作了保留，应该有其可用之处。我这里说的谨慎，是因为级联删除和级联更新有些突破了传统的关于外键的定义，功能有点太过强大，使用前必须确定自己已经把握好其功能范围，否则，级联删除和级联更新可能让你的数据莫名其妙的被修改或者丢失。从性能看级联删除和级联更新是比其他方法更高效的方法。</p>
<p>5、 字段的设计</p>
<p>字段是数据库最基本的单位，其设计对性能的影响是很大的。需要注意如下：</p>
<p>A、数据类型尽量用数字型，数字型的比较比字符型的快很多。</p>
<p>B、 数据类型尽量小，这里的尽量小是指在满足可以预见的未来需求的前提下的。</p>
<p>C、 尽量不要允许NULL，除非必要，可以用NOT NULL+DEFAULT代替。</p>
<p>D、少用TEXT和IMAGE，二进制字段的读写是比较慢的，而且，读取的方法也不多，大部分情况下最好不用。</p>
<p>E、 自增字段要慎用，不利于数据迁移。</p>
<p>&nbsp;</p>
<p>6、 数据库物理存储和环境的设计</p>
<p>在设计阶段，可以对数据库的物理存储、操作系统环境、网络环境进行必要的设计，使得我们的系统在将来能适应比较多的用户并发和比较大的数据量。</p>
<p>这里需要注意文件组的作用，适用文件组可以有效把I/O操作分散到不同的物理硬盘，提高并发能力。</p>
<p>7、 系统设计</p>
<p>整个系统的设计特别是系统结构设计对性能是有很大影响的，对于一般的OLTP系统，可以选择C/S结构、三层的C/S结构等，不同的系统结构其性能的关键也有所不同。</p>
<p>系统设计阶段应该归纳一些业务逻辑放在数据库编程实现，数据库编程包括数据库存储过程、触发器和函数。用数据库编程实现业务逻辑的好处是减少网络流量并可更充分利用数据库的预编译和缓存功能。</p>
<p>8、 索引的设计</p>
<p>在设计阶段，可以根据功能和性能的需求进行初步的索引设计，这里需要根据预计的数据量和查询来设计索引，可能与将来实际使用的时候会有所区别。</p>
<p>关于索引的选择，应改主意：</p>
<p>A、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据数据量决定哪些表需要增加索引，数据量小的可以只有主键。</p>
<p>B、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据使用频率决定哪些字段需要建立索引，选择经常作为连接条件、筛选条件、聚合查询、排序的字段作为索引的候选字段。</p>
<p>C、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把经常一起出现的字段组合在一起，组成组合索引，组合索引的字段顺序与主键一样，也需要把最常用的字段放在前面，把重复率低的字段放在前面。</p>
<p>D、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个表不要加太多索引，因为索引影响插入和更新的速度。</p>
<p>&nbsp;</p>
<p>三、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编码阶段</p>
<p>编码阶段是本文的重点，因为在设计确定的情况下，编码的质量几乎决定了整个系统的质量。</p>
<p>编码阶段首先是需要所有程序员有性能意识，也就是在实现功能同时有考虑性能的思想，数据库是能进行集合运算的工具，我们应该尽量的利用这个工具，所谓集合运算实际是批量运算，就是尽量减少在客户端进行大数据量的循环操作，而用SQL语句或者存储过程代替。关于思想和意识，很难说得很清楚，需要在编程过程中来体会。</p>
<p>下面罗列一些编程阶段需要注意的事项：</p>
<p>1、 只返回需要的数据</p>
<p>返回数据到客户端至少需要数据库提取数据、网络传输数据、客户端接收数据以及客户端处理数据等环节，如果返回不需要的数据，就会增加服务器、网络和客户端的无效劳动，其害处是显而易见的，避免这类事件需要注意：</p>
<p>A、横向来看，不要写SELECT *的语句，而是选择你需要的字段。</p>
<p>B、 纵向来看，合理写WHERE子句，不要写没有WHERE的SQL语句。</p>
<p>C、 注意SELECT INTO后的WHERE子句，因为SELECT INTO把数据插入到临时表，这个过程会锁定一些系统表，如果这个WHERE子句返回的数据过多或者速度太慢，会造成系统表长期锁定，诸塞其他进程。</p>
<p>D、对于聚合查询，可以用HAVING子句进一步限定返回的行。</p>
<p>2、 尽量少做重复的工作</p>
<p>这一点和上一点的目的是一样的，就是尽量减少无效工作，但是这一点的侧重点在客户端程序，需要注意的如下：</p>
<p>A、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 控制同一语句的多次执行，特别是一些基础数据的多次执行是很多程序员很少注意的。</p>
<p>B、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 减少多次的数据转换，也许需要数据转换是设计的问题，但是减少次数是程序员可以做到的。</p>
<p>C、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 杜绝不必要的子查询和连接表，子查询在执行计划一般解释成外连接，多余的连接表带来额外的开销。</p>
<p>D、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 合并对同一表同一条件的多次UPDATE，比如</p>
<p>UPDATE EMPLOYEE SET FNAME=&#8217;HAIWER&#8217; WHERE EMP_ID=&#8217; VPA30890F&#8217; </p>
<p>UPDATE EMPLOYEE SET LNAME=&#8217;YANG&#8217; WHERE EMP_ID=&#8217; VPA30890F&#8217; </p>
<p><br />这两个语句应该合并成以下一个语句</p>
<p>UPDATE EMPLOYEE SET FNAME=&#8217;HAIWER&#8217;,LNAME=&#8217;YANG&#8217;&nbsp; <br />WHERE EMP_ID=&#8217; VPA30890F&#8217;<br />E、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UPDATE操作不要拆成DELETE操作+INSERT操作的形式，虽然功能相同，但是性能差别是很大的。</p>
<p>F、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不要写一些没有意义的查询，比如</p>
<p>&nbsp;&nbsp;&nbsp; SELECT * FROM EMPLOYEE WHERE 1=2</p>
<p>3、 注意事务和锁</p>
<p>事务是数据库应用中和重要的工具，它有原子性、一致性、隔离性、持久性这四个属性，很多操作我们都需要利用事务来保证数据的正确性。在使用事务中我们需要做到尽量避免死锁、尽量减少阻塞。具体以下方面需要特别注意：</p>
<p>A、事务操作过程要尽量小，能拆分的事务要拆分开来。</p>
<p>B、 事务操作过程不应该有交互，因为交互等待的时候，事务并未结束，可能锁定了很多资源。</p>
<p>C、 事务操作过程要按同一顺序访问对象。</p>
<p>D、提高事务中每个语句的效率，利用索引和其他方法提高每个语句的效率可以有效地减少整个事务的执行时间。</p>
<p>E、 尽量不要指定锁类型和索引，SQL SERVER允许我们自己指定语句使用的锁类型和索引，但是一般情况下，SQL SERVER优化器选择的锁类型和索引是在当前数据量和查询条件下是最优的，我们指定的可能只是在目前情况下更有，但是数据量和数据分布在将来是会变化的。</p>
<p>F、 查询时可以用较低的隔离级别，特别是报表查询的时候，可以选择最低的隔离级别（未提交读）。</p>
<p>4、 注意临时表和表变量的用法</p>
<p>在复杂系统中，临时表和表变量很难避免，关于临时表和表变量的用法，需要注意：</p>
<p>A、如果语句很复杂，连接太多，可以考虑用临时表和表变量分步完成。</p>
<p>B、 如果需要多次用到一个大表的同一部分数据，考虑用临时表和表变量暂存这部分数据。</p>
<p>C、 如果需要综合多个表的数据，形成一个结果，可以考虑用临时表和表变量分步汇总这多个表的数据。</p>
<p>D、其他情况下，应该控制临时表和表变量的使用。</p>
<p>E、 关于临时表和表变量的选择，很多说法是表变量在内存，速度快，应该首选表变量，但是在实际使用中发现，这个选择主要考虑需要放在临时表的数据量，在数据量较多的情况下，临时表的速度反而更快。</p>
<p>F、 关于临时表产生使用SELECT INTO和CREATE TABLE + INSERT INTO的选择，我们做过测试，一般情况下，SELECT INTO会比CREATE TABLE + INSERT INTO的方法快很多，但是SELECT INTO会锁定TEMPDB的系统表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS，在多用户并发环境下，容易阻塞其他进程，所以我的建议是，在并发系统中，尽量使用CREATE TABLE + INSERT INTO，而大数据量的单个语句使用中，使用SELECT INTO。</p>
<p>G、&nbsp; 注意排序规则，用CREATE TABLE建立的临时表，如果不指定字段的排序规则，会选择TEMPDB的默认排序规则，而不是当前数据库的排序规则。如果当前数据库的排序规则和TEMPDB的排序规则不同，连接的时候就会出现排序规则的冲突错误。一般可以在CREATE TABLE建立临时表时指定字段的排序规则为DATABASE_DEFAULT来避免上述问题。</p>
<p>5、 子查询的用法</p>
<p>子查询是一个 SELECT 查询，它嵌套在 SELECT、INSERT、UPDATE、DELETE 语句或其它子查询中。任何允许使用表达式的地方都可以使用子查询。</p>
<p>子查询可以使我们的编程灵活多样，可以用来实现一些特殊的功能。但是在性能上，往往一个不合适的子查询用法会形成一个性能瓶颈。</p>
<p>如果子查询的条件中使用了其外层的表的字段，这种子查询就叫作相关子查询。相关子查询可以用IN、NOT IN、EXISTS、NOT EXISTS引入。</p>
<p>关于相关子查询，应该注意：</p>
<p>A、NOT IN、NOT EXISTS的相关子查询可以改用LEFT JOIN代替写法。比如：</p>
<p>&nbsp;</p>
<p>SELECT PUB_NAME <br />FROM PUBLISHERS <br />WHERE PUB_ID NOT IN <br />&nbsp;&nbsp; (SELECT PUB_ID <br />&nbsp;&nbsp; FROM TITLES <br />&nbsp;&nbsp; WHERE TYPE = 'BUSINESS') <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以改写成：</p>
<p>SELECT A.PUB_NAME <br />FROM PUBLISHERS A LEFT JOIN TITLES B <br />ON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B.TYPE = 'BUSINESS' AND <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A.PUB_ID=B. PUB_ID <br />WHERE B.PUB_ID IS NULL<br />&nbsp;</p>
<p>SELECT TITLE <br />FROM TITLES <br />WHERE NOT EXISTS <br />&nbsp;&nbsp; (SELECT TITLE_ID <br />&nbsp;&nbsp; FROM SALES <br />&nbsp;&nbsp; WHERE TITLE_ID = TITLES.TITLE_ID)<br />可以改写成：</p>
<p>SELECT TITLE <br />FROM TITLES LEFT JOIN SALES <br />ON SALES.TITLE_ID = TITLES.TITLE_ID <br />WHERE SALES.TITLE_ID IS NULL<br />B、 如果保证子查询没有重复 ，IN、EXISTS的相关子查询可以用INNER JOIN 代替。比如：</p>
<p>SELECT PUB_NAME <br />FROM PUBLISHERS <br />WHERE PUB_ID IN <br />&nbsp;&nbsp; (SELECT PUB_ID <br />&nbsp;&nbsp; FROM TITLES <br />&nbsp;&nbsp; WHERE TYPE = 'BUSINESS')<br />可以改写成：</p>
<p>SELECT DISTINCT A.PUB_NAME <br />FROM PUBLISHERS A INNER JOIN TITLES B <br />ON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B.TYPE = 'BUSINESS' AND <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A.PUB_ID=B. PUB_ID</p>
<p>C、 IN的相关子查询用EXISTS代替，比如</p>
<p>SELECT PUB_NAME <br />FROM PUBLISHERS <br />WHERE PUB_ID IN <br />&nbsp;&nbsp; (SELECT PUB_ID <br />&nbsp;&nbsp; FROM TITLES <br />&nbsp;&nbsp; WHERE TYPE = 'BUSINESS')<br />可以用下面语句代替：</p>
<p>SELECT PUB_NAME <br />FROM PUBLISHERS <br />WHERE EXISTS <br />&nbsp;&nbsp; (SELECT 1 <br />&nbsp;&nbsp; FROM TITLES <br />&nbsp;&nbsp; WHERE TYPE = 'BUSINESS' AND <br />&nbsp;&nbsp; PUB_ID= PUBLISHERS.PUB_ID)<br />D、不要用COUNT(*)的子查询判断是否存在记录，最好用LEFT JOIN或者EXISTS，比如有人写这样的语句：</p>
<p>SELECT JOB_DESC FROM JOBS <br />WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)=0<br />应该改成：</p>
<p>SELECT JOBS.JOB_DESC FROM JOBS LEFT JOIN EMPLOYEE&nbsp; <br />ON EMPLOYEE.JOB_ID=JOBS.JOB_ID <br />WHERE EMPLOYEE.EMP_ID IS NULL<br />&nbsp;</p>
<p>SELECT JOB_DESC FROM JOBS <br />WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)&lt;&gt;0<br />应该改成：</p>
<p>SELECT JOB_DESC FROM JOBS <br />WHERE EXISTS (SELECT 1 FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)<br />6、 慎用游标</p>
<p>数据库一般的操作是集合操作，也就是对由WHERE子句和选择列确定的结果集作集合操作，游标是提供的一个非集合操作的途径。一般情况下，游标实现的功能往往相当于客户端的一个循环实现的功能，所以，大部分情况下，我们把游标功能搬到客户端。</p>
<p>游标是把结果集放在服务器内存，并通过循环一条一条处理记录，对数据库资源（特别是内存和锁资源）的消耗是非常大的，所以，我们应该只有在没有其他方法的情况下才使用游标。</p>
<p>另外，我们可以用SQL SERVER的一些特性来代替游标，达到提高速度的目的。</p>
<p>A、字符串连接的例子</p>
<p>这是论坛经常有的例子，就是把一个表符合条件的记录的某个字符串字段连接成一个变量。比如需要把JOB_ID=10的EMPLOYEE的FNAME连接在一起，用逗号连接，可能最容易想到的是用游标：</p>
<p>&nbsp;&nbsp;&nbsp; DECLARE @NAME VARCHAR(20) <br />&nbsp;&nbsp;&nbsp; DECLARE @NAME VARCHAR(1000) <br />&nbsp;&nbsp;&nbsp; DECLARE NAME_CURSOR CURSOR FOR <br />&nbsp;&nbsp;&nbsp; SELECT FNAME FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID <br />&nbsp;&nbsp;&nbsp; OPEN NAME_CURSOR <br />&nbsp;&nbsp;&nbsp; FETCH NEXT FROM RNAME_CURSOR INTO @NAME <br />&nbsp;&nbsp;&nbsp; WHILE @@FETCH_STATUS = 0 <br />&nbsp;&nbsp;&nbsp; BEGIN <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SET @NAMES = ISNULL(@NAMES+&#8217;,&#8217;,&#8217;&#8217;)+@NAME <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FETCH NEXT FROM NAME_CURSOR&nbsp; INTO @NAME&nbsp; <br />&nbsp;&nbsp;&nbsp; END <br />&nbsp;&nbsp;&nbsp; CLOSE NAME_CURSOR <br />&nbsp;&nbsp;&nbsp; DEALLOCATE NAME_CURSOR<br />可以如下修改，功能相同：</p>
<p>&nbsp;&nbsp; DECLARE @NAME VARCHAR(1000) <br />&nbsp;&nbsp; SELECT @NAMES = ISNULL(@NAMES+&#8217;,&#8217;,&#8217;&#8217;)+FNAME <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID<br />B、 用CASE WHEN 实现转换的例子</p>
<p>很多使用游标的原因是因为有些处理需要根据记录的各种情况需要作不同的处理，实际上这种情况，我们可以用CASE WHEN语句进行必要的判断处理，而且CASE WHEN是可以嵌套的。比如:</p>
<p>表结构:</p>
<p>CREATE TABLE 料件表( <br />料号&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VARCHAR(30), <br />名称&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VARCHAR(100), <br />主单位&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VARCHAR(20), <br />单位1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VARCHAR(20), <br />单位1参数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NUMERIC(18,4), <br />单位2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VARCHAR(20), <br />单位2参数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NUMERIC(18,4) <br />) </p>
<p>GO </p>
<p>CREATE TABLE 入库表( <br />时间&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATETIME, <br />料号&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VARCHAR(30), <br />单位&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT, <br />入库数量&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NUMERIC(18,4), <br />损坏数量&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NUMERIC(18,4) <br />) </p>
<p>GO<br />其中，单位字段可以是0，1，2，分别代表主单位、单位1、单位2，很多计算需要统一单位，统一单位可以用游标实现：</p>
<p>DECLARE @料号&nbsp;&nbsp;&nbsp;&nbsp; VARCHAR(30), <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @单位&nbsp;&nbsp; INT, <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @参数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NUMERIC(18,4), </p>
<p>DECLARE CUR CURSOR FOR <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT 料号,单位 FROM 入库表 WHERE 单位 &lt;&gt;0 <br />OPEN CUR <br />FETCH NEXT FROM CUR INTO @料号,@单位 <br />WHILE @@FETCH_STATUS&lt;&gt;-1 <br />BEGIN <br />&nbsp; IF @单位=1 <br />&nbsp; BEGIN <br />&nbsp;&nbsp;&nbsp; SET @参数=(SELECT 单位1参数 FROM 料件表 WHERE 料号 =@料号) <br />&nbsp;&nbsp;&nbsp; UPDATE 入库表 SET 数量=数量*@参数,损坏数量=损坏数量*@参数,单位=1 WHERE CURRENT OF CUR <br />&nbsp; END <br />&nbsp; IF @单位=2 <br />&nbsp; BEGIN <br />&nbsp;&nbsp;&nbsp; SET @参数=(SELECT 单位1参数 FROM 料件表 WHERE 料号 =@料号) <br />&nbsp;&nbsp;&nbsp; UPDATE 入库表 SET 数量=数量*@参数,损坏数量=损坏数量*@参数,单位=1 WHERE CURRENT OF CUR <br />&nbsp; END <br />&nbsp; FETCH NEXT FROM CUR INTO @料号,@单位 <br />END <br />CLOSE CUR <br />DEALLOCATE CUR<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以改写成：</p>
<p>UPDATE A SET&nbsp; <br />数量=CASE A.单位 WHEN 1 THEN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A.数量*B. 单位1参数 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WHEN 2 THEN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A.数量*B. 单位2参数 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ELSE A.数量 <br />END,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />损坏数量= CASE A.单位 WHEN 1 THEN&nbsp;&nbsp;&nbsp; A. 损坏数量*B. 单位1参数 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WHEN 2 THEN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A. 损坏数量*B. 单位2参数 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ELSE A. 损坏数量 <br />END, <br />单位=1&nbsp; <br />FROM入库表 A, 料件表 B <br />WHERE&nbsp;&nbsp;&nbsp; A.单位&lt;&gt;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AND <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A.料号=B.料号<br />C、 变量参与的UPDATE语句的例子</p>
<p>SQL ERVER的语句比较灵活，变量参与的UPDATE语句可以实现一些游标一样的功能，比如：</p>
<p>在</p>
<p>SELECT A,B,C,CAST(NULL AS INT) AS 序号 <br />INTO #T <br />FROM 表 <br />ORDER BY A ,NEWID()<br />产生临时表后，已经按照A字段排序，但是在A相同的情况下是乱序的，这时如果需要更改序号字段为按照A字段分组的记录序号，就只有游标和变量参与的UPDATE语句可以实现了，这个变量参与的UPDATE语句如下：</p>
<p>DECLARE @A INT <br />DECLARE @序号 INT <br />UPDATE #T SET <br />&nbsp;&nbsp; @序号=CASE WHEN <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#65;&#61;&#64;&#65;"><font color="#336699">A=@A</font></a> THEN @序号+1 ELSE 1 END, <br />&nbsp;&nbsp; @A=A, <br />&nbsp;&nbsp; 序号=@序号<br />D、如果必须使用游标，注意选择游标的类型，如果只是循环取数据，那就应该用只进游标（选项FAST_FORWARD），一般只需要静态游标（选项STATIC）。</p>
<p>E、 注意动态游标的不确定性，动态游标查询的记录集数据如果被修改，会自动刷新游标，这样使得动态游标有了不确定性，因为在多用户环境下，如果其他进程或者本身更改了纪录，就可能刷新游标的记录集。</p>
<p>7、 尽量使用索引</p>
<p>建立索引后，并不是每个查询都会使用索引，在使用索引的情况下，索引的使用效率也会有很大的差别。只要我们在查询语句中没有强制指定索引，索引的选择和使用方法是SQLSERVER的优化器自动作的选择，而它选择的根据是查询语句的条件以及相关表的统计信息，这就要求我们在写SQL语句的时候尽量使得优化器可以使用索引。</p>
<p>为了使得优化器能高效使用索引，写语句的时候应该注意：</p>
<p>A、不要对索引字段进行运算，而要想办法做变换，比如</p>
<p>SELECT ID FROM T WHERE NUM/2=100</p>
<p>应改为:</p>
<p>SELECT ID FROM T WHERE NUM=100*2</p>
<p>&nbsp;</p>
<p>SELECT ID FROM T WHERE NUM/2=NUM1</p>
<p>如果NUM有索引应改为:</p>
<p>SELECT ID FROM T WHERE NUM=NUM1*2</p>
<p>如果NUM1有索引则不应该改。</p>
<p>&nbsp;</p>
<p>发现过这样的语句：</p>
<p>SELECT 年,月,金额 FROM 结余表 <br />WHERE 100*年+月=2007*100+10<br />应该改为：</p>
<p>SELECT 年,月,金额 FROM 结余表 <br />WHERE 年=2007 AND <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 月=10<br />&nbsp;</p>
<p>B、 不要对索引字段进行格式转换</p>
<p>日期字段的例子：</p>
<p>WHERE CONVERT(VARCHAR(10), 日期字段,120)=&#8217;2008-08-15&#8217;</p>
<p>应该改为</p>
<p>WHERE日期字段〉=&#8217;2008-08-15&#8217;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AND&nbsp;&nbsp; 日期字段&lt;&#8217;2008-08-16&#8217;</p>
<p>&nbsp;</p>
<p>ISNULL转换的例子：</p>
<p>WHERE ISNULL(字段,&#8217;&#8217;)&lt;&gt;&#8217;&#8217;应改为:WHERE字段&lt;&gt;&#8217;&#8217;</p>
<p>WHERE ISNULL(字段,&#8217;&#8217;)=&#8217;&#8217;不应修改</p>
<p>WHERE ISNULL(字段,&#8217;F&#8217;) =&#8217;T&#8217;应改为: WHERE字段=&#8217;T&#8217;</p>
<p>WHERE ISNULL(字段,&#8217;F&#8217;)&lt;&gt;&#8217;T&#8217;不应修改</p>
<p>C、 不要对索引字段使用函数</p>
<p>WHERE LEFT(NAME, 3)='ABC' 或者WHERE SUBSTRING(NAME,1, 3)='ABC'</p>
<p>应改为: </p>
<p>WHERE NAME LIKE 'ABC%'</p>
<p>&nbsp;</p>
<p>日期查询的例子：</p>
<p>WHERE DATEDIFF(DAY, 日期,'2005-11-30')=0应改为:WHERE 日期 &gt;='2005-11-30' AND 日期 &lt;'2005-12-1&#8216;</p>
<p>WHERE DATEDIFF(DAY, 日期,'2005-11-30')&gt;0应改为:WHERE 日期 &lt;'2005-11-30&#8216;</p>
<p>WHERE DATEDIFF(DAY, 日期,'2005-11-30')&gt;=0应改为:WHERE 日期 &lt;'2005-12-01&#8216;</p>
<p>WHERE DATEDIFF(DAY, 日期,'2005-11-30')&lt;0应改为:WHERE 日期&gt;='2005-12-01&#8216;</p>
<p>WHERE DATEDIFF(DAY, 日期,'2005-11-30')&lt;=0应改为:WHERE 日期&gt;='2005-11-30&#8216;</p>
<p>D、不要对索引字段进行多字段连接</p>
<p>比如：</p>
<p>WHERE FAME+ &#8217;.&#8217;+LNAME=&#8216;HAIWEI.YANG&#8217; </p>
<p>应改为: </p>
<p>WHERE FNAME=&#8216;HAIWEI&#8217; AND LNAME=&#8216;YANG&#8217;</p>
<p>8、 注意连接条件的写法</p>
<p>多表连接的连接条件对索引的选择有着重要的意义，所以我们在写连接条件条件的时候需要特别的注意。</p>
<p>A、多表连接的时候，连接条件必须写全，宁可重复，不要缺漏。</p>
<p>B、 连接条件尽量使用聚集索引</p>
<p>C、 注意ON部分条件和WHERE部分条件的区别</p>
<p>&nbsp;</p>
<p>9、 其他需要注意的地方</p>
<p>经验表明，问题发现的越早解决的成本越低，很多性能问题可以在编码阶段就发现，为了提早发现性能问题，需要注意：</p>
<p>A、程序员注意、关心各表的数据量。</p>
<p>B、 编码过程和单元测试过程尽量用数据量较大的数据库测试，最好能用实际数据测试。</p>
<p>C、 每个SQL语句尽量简单</p>
<p>D、不要频繁更新有触发器的表的数据</p>
<p>E、 注意数据库函数的限制以及其性能</p>
<p>10、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 学会分辩SQL语句的优劣</p>
<p>自己分辨SQL语句的优劣非常重要，只有自己能分辨优劣才能写出高效的语句。</p>
<p>A、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 查看SQL语句的执行计划，可以在查询分析其使用CTRL+L图形化的显示执行计划，一般应该注意百分比最大的几个图形的属性，把鼠标移动到其上面会显示这个图形的属性，需要注意预计成本的数据，也要注意其标题，一般都是CLUSTERED INDEX SEEK 、INDEX SEEK 、CLUSTERED INDEX SCAN 、INDEX SCAN 、TABLE SCAN等，其中出现SCAN说明语句有优化的余地。也可以用语句</p>
<p>SET SHOWPLAN_ALL ON</p>
<p>要执行的语句</p>
<p>SET SHOWPLAN_ALL OFF</p>
<p>查看执行计划的文本详细信息。</p>
<p>B、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用事件探查器跟踪系统的运行，可疑跟踪到执行的语句，以及所用的时间，CPU用量以及I/O数据，从而分析语句的效率。</p>
<p>C、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以用WINDOWS的系统性能检测器，关注CPU、I/O参数</p>
<p>四、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试、试运行、维护阶段</p>
<p>测试的主要任务是发现并修改系统的问题，其中性能问题也是一个重要的方面。重点应该放在发现有性能问题的地方，并进行必要的优化。主要进行语句优化、索引优化等。</p>
<p>试运行和维护阶段是在实际的环境下运行系统，发现的问题范围更广，可能涉及操作系统、网络以及多用户并发环境出现的问题，其优化也扩展到操作系统、网络以及数据库物理存储的优化。</p>
<p>这个阶段的优花方法在这里不再展开，只说明下索引维护的方法：</p>
<p>A、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以用DBCC DBREINDEX语句或者SQL SERVER维护计划设定定时进行索引重建，索引重建的目的是提高索引的效能。</p>
<p>B、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以用语句UPDATE STATISTICS或者SQL SERVER维护计划设定定时进行索引统计信息的更新，其目的是使得统计信息更能反映实际情况，从而使得优化器选择更合适的索引。</p>
<p>C、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以用DBCC CHECKDB或者DBCC CHECKTABLE语句检查数据库表和索引是否有问题，这两个语句也能修复一般的问题。</p>
<p>D、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>五、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 网上资料中一些说法的个人不同意见</p>
<p>1、 &#8220;应尽量避免在 WHERE 子句中对字段进行 NULL 值判断，否则将导致引擎放弃使用索引而进行全表扫描，如：</p>
<p>SELECT ID FROM T WHERE NUM IS NULL</p>
<p>可以在NUM上设置默认值0，确保表中NUM列没有NULL值，然后这样查询：</p>
<p>SELECT ID FROM T WHERE NUM=0&#8221;</p>
<p>个人意见：经过测试，IS NULL也是可以用INDEX SEEK查找的，0和NULL是不同概念的，以上说法的两个查询的意义和记录数是不同的。</p>
<p>2、 &#8220;应尽量避免在 WHERE 子句中使用!=或&lt;&gt;操作符，否则将引擎放弃使用索引而进行全表扫描。&#8221;</p>
<p>个人意见：经过测试，&lt;&gt;也是可以用INDEX SEEK查找的。</p>
<p>3、 &#8220;应尽量避免在 WHERE 子句中使用 OR 来连接条件，否则将导致引擎放弃使用索引而进行全表扫描，如：</p>
<p>SELECT ID FROM T WHERE NUM=10 OR NUM=20</p>
<p>可以这样查询：</p>
<p>SELECT ID FROM T WHERE NUM=10</p>
<p>UNION ALL</p>
<p>SELECT ID FROM T WHERE NUM=20&#8221;</p>
<p>个人意见：主要对全表扫描的说法不赞同。</p>
<p>4、 &#8220;IN 和 NOT IN 也要慎用，否则会导致全表扫描，如：</p>
<p>SELECT ID FROM T WHERE NUM IN(1,2,3)</p>
<p>对于连续的数值，能用 BETWEEN 就不要用 IN 了：</p>
<p>SELECT ID FROM T WHERE NUM BETWEEN 1 AND 3&#8221;</p>
<p>个人意见：主要对全表扫描的说法不赞同。</p>
<p>5、 &#8220;如果在 WHERE 子句中使用参数，也会导致全表扫描。因为SQL只有在运行时才会解析局部变量，但优化程序不能将访问计划的选择推迟到运行时；它必须在编译时进行选择。然而，如果在编译时建立访问计划，变量的值还是未知的，因而无法作为索引选择的输入项。如下面语句将进行全表扫描：</p>
<p>SELECT ID FROM T WHERE <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#78;&#85;&#77;&#61;&#64;&#78;&#85;&#77;"><font color="#336699">NUM=@NUM</font></a></p>
<p>可以改为强制查询使用索引：</p>
<p>SELECT ID FROM T WITH(INDEX(索引名)) WHERE <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#78;&#85;&#77;&#61;&#64;&#78;&#85;&#77;"><font color="#336699">NUM=@NUM</font></a>&#8221;</p>
<p>个人意见：关于局部变量的解释比较奇怪，使用参数如果会影响性能，那存储过程就该校除了，我坚持我上面对于强制索引的看法。</p>
<p>6、 &#8220;尽可能的使用 VARCHAR/NVARCHAR 代替 CHAR/NCHAR ，因为首先变长字段存储空间小，可以节省存储空间，其次对于查询来说，在一个相对较小的字段内搜索效率显然要高些。&#8221;</p>
<p>个人意见：&#8220;在一个相对较小的字段内搜索效率显然要高些&#8221;显然是对的，但是字段的长短似乎不是由变不变长决定，而是业务本身决定。在SQLSERVER6.5或者之前版本，不定长字符串字段的比较速度比定长的字符串字段的比较速度慢很多，所以对于那些版本，我们都是推荐使用定长字段存储一些关键字段。而在2000版本，修改了不定长字符串字段的比较方法，与定长字段的比较速度差别不大了，这样为了方便，我们大量使用不定长字段。</p>
<p>7、 关于连接表的顺序或者条件的顺序的说法，经过测试，在SQL SERVER，这些顺序都是不影响性能的，这些说法可能是对ORACLE有效。</p><img src ="http://www.blogjava.net/AndyZhang/aggbug/356875.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AndyZhang/" target="_blank">SkyDream</a> 2011-08-19 16:56 <a href="http://www.blogjava.net/AndyZhang/archive/2011/08/19/356875.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MS SQL Server查询优化方法 </title><link>http://www.blogjava.net/AndyZhang/archive/2011/08/19/356874.html</link><dc:creator>SkyDream</dc:creator><author>SkyDream</author><pubDate>Fri, 19 Aug 2011 08:54:00 GMT</pubDate><guid>http://www.blogjava.net/AndyZhang/archive/2011/08/19/356874.html</guid><description><![CDATA[<div class="article_content">
<p><span style="color: #000000">MS&nbsp;&nbsp; SQL&nbsp;&nbsp; Server查询优化方法<br />查询速度慢的原因很多，常见如下几种&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">1</span><span style="color: #000000">、没有索引或者没有用到索引(这是查询慢最常见的问题，是程序设计的缺陷)&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">2</span><span style="color: #000000">、I</span><span style="color: #808080">/</span><span style="color: #000000">O吞吐量小，形成了瓶颈效应。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">3</span><span style="color: #000000">、没有创建计算列导致查询不优化。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">4</span><span style="color: #000000">、内存不足&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">5</span><span style="color: #000000">、网络速度慢&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">6</span><span style="color: #000000">、查询出的数据量过大（可以采用多次查询，其他的方法降低数据量）&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">7</span><span style="color: #000000">、锁或者死锁(这也是查询慢最常见的问题，是程序设计的缺陷)&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">8</span><span style="color: #000000">、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">9</span><span style="color: #000000">、返回了不必要的行和列&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">10</span><span style="color: #000000">、查询语句不好，没有优化&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以通过如下方法来优化查询&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">1</span><span style="color: #000000">、把数据、日志、索引放到不同的I</span><span style="color: #808080">/</span><span style="color: #000000">O设备上，增加读取速度，以前可以将Tempdb应放在RAID0上，SQL2000不在支持。数据量（尺寸）越大，提高I</span><span style="color: #808080">/</span><span style="color: #000000">O越重要.&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">2</span><span style="color: #000000">、纵向、横向分割表，减少表的尺寸(sp_spaceuse)&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">3</span><span style="color: #000000">、升级硬件&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">4</span><span style="color: #000000">、根据查询条件,建立索引,优化索引、优化访问方式，限制结果集的数据量。注意填充因子要适当（最好是使用默认值0）。索引应该尽量小，使用字节数小的列建索引好（参照索引的创建）,不要对有限的几个值的字段建单一索引如性别字段&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">5</span><span style="color: #000000">、提高网速;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">6</span><span style="color: #000000">、扩大服务器的内存,Windows&nbsp;&nbsp; 2000和SQL&nbsp;&nbsp; server&nbsp;&nbsp; 2000能支持4</span><span style="color: #808080">-</span><span style="color: #000000">8G的内存。配置虚拟内存：虚拟内存大小应基于计算机上并发运行的服务进行配置。运行&nbsp;&nbsp; Microsoft&nbsp;&nbsp; SQL&nbsp;&nbsp; Server?&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">2000</span><span style="color: #000000">&nbsp;&nbsp; 时，可考虑将虚拟内存大小设置为计算机中安装的物理内存的&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">1.5</span><span style="color: #000000">&nbsp;&nbsp; 倍。如果另外安装了全文检索功能，并打算运行&nbsp;&nbsp; Microsoft&nbsp;&nbsp; 搜索服务以便执行全文索引和查询，可考虑：将虚拟内存大小配置为至少是计算机中安装的物理内存的&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">3</span><span style="color: #000000">&nbsp;&nbsp; 倍。将&nbsp;&nbsp; SQL&nbsp;&nbsp; Server&nbsp;&nbsp; </span><span style="color: #ff00ff">max</span><span style="color: #000000">&nbsp;&nbsp; server&nbsp;&nbsp; memory&nbsp;&nbsp; 服务器配置选项配置为物理内存的&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">1.5</span><span style="color: #000000">&nbsp;&nbsp; 倍（虚拟内存大小设置的一半）。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">7</span><span style="color: #000000">、增加服务器CPU个数;但是必须明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MsSQL自动评估选择的。单个任务分解成多个任务，就可以在处理器上运行。例如耽搁查询的排序、连接、扫描和GROUP&nbsp;&nbsp; BY字句同时执行，SQL&nbsp;&nbsp; SERVER根据系统的负载情况决定最优的并行等级，复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作UPDATE,</span><span style="color: #0000ff">INSERT</span><span style="color: #000000">， DELETE还不能并行处理。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">8</span><span style="color: #000000">、如果是使用like进行查询的话，简单的使用index是不行的，但是全文索引，耗空间。&nbsp;&nbsp; </span><span style="color: #808080">like</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">a%</span><span style="color: #ff0000">'</span><span style="color: #000000">&nbsp;&nbsp; 使用索引&nbsp;&nbsp; </span><span style="color: #808080">like</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">%a</span><span style="color: #ff0000">'</span><span style="color: #000000">&nbsp;&nbsp; 不使用索引用&nbsp;&nbsp; </span><span style="color: #808080">like</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">%a%</span><span style="color: #ff0000">'</span><span style="color: #000000">&nbsp;&nbsp; 查询时，查询耗时和字段值总长度成正比,所以不能用CHAR类型，而是VARCHAR。对于字段的值很长的建全文索引。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">9</span><span style="color: #000000">、DB&nbsp;&nbsp; Server&nbsp;&nbsp; 和APPLication&nbsp;&nbsp; Server&nbsp;&nbsp; 分离；OLTP和OLAP分离&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">10</span><span style="color: #000000">、分布式分区视图可用于实现数据库服务器联合体。联合体是一组分开管理的服务器，但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器，以支持大型的多层&nbsp;&nbsp; Web&nbsp;&nbsp; 站点的处理需要。有关更多信息，参见设计联合数据库服务器。（参照SQL帮助文件</span><span style="color: #ff0000">'</span><span style="color: #ff0000">分区视图</span><span style="color: #ff0000">'</span><span style="color: #000000">）&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a、在实现分区视图之前，必须先水平分区表&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b、在创建成员表后，在每个成员服务器上定义一个分布式分区视图，并且每个视图具有相同的名称。这样，引用分布式分区视图名的查询可以在任何一个成员服务器上运行。系统操作如同每个成员服务器上都有一个原始表的复本一样，但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">11</span><span style="color: #000000">、重建索引&nbsp;&nbsp; </span><span style="color: #0000ff">DBCC</span><span style="color: #000000">&nbsp;&nbsp; REINDEX&nbsp;&nbsp; ,</span><span style="color: #0000ff">DBCC</span><span style="color: #000000">&nbsp;&nbsp; INDEXDEFRAG,收缩数据和日志&nbsp;&nbsp; </span><span style="color: #0000ff">DBCC</span><span style="color: #000000">&nbsp;&nbsp; SHRINKDB,</span><span style="color: #0000ff">DBCC</span><span style="color: #000000">&nbsp;&nbsp; SHRINKFILE.&nbsp;&nbsp; 设置自动收缩日志.对于大的数据库不要设置数据库自动增长，它会降低服务器的性能。&nbsp;&nbsp; 在T</span><span style="color: #808080">-</span><span style="color: #000000">sql的写法上有很大的讲究，下面列出常见的要点：首先，DBMS处理查询计划的过程是这样的：&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">(1)</span><span style="color: #000000">、&nbsp;&nbsp; 查询语句的词法、语法检查&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">2)</span><span style="color: #000000">、&nbsp;&nbsp; 将语句提交给DBMS的查询优化器&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">3)</span><span style="color: #000000">、&nbsp;&nbsp; 优化器做代数优化和存取路径的优化&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">4)</span><span style="color: #000000">、&nbsp;&nbsp; 由预编译模块生成查询规划&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">5)</span><span style="color: #000000">、&nbsp;&nbsp; 然后在合适的时间提交给系统处理执行&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">6)</span><span style="color: #000000">、&nbsp;&nbsp; 最后将执行结果返回给用户其次，看一下SQL&nbsp;&nbsp; SERVER的数据存放的结构：一个页面的大小为8K(</span><span style="font-weight: bold; color: #800000">8060</span><span style="color: #000000">)字节，8个页面为一个盘区，按照B树存放。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">12</span><span style="color: #000000">、Commit和rollback的区别&nbsp;&nbsp; </span><span style="color: #0000ff">Rollback</span><span style="color: #000000">:回滚所有的事物。&nbsp;&nbsp; </span><span style="color: #0000ff">Commit</span><span style="color: #000000">:提交当前的事物.&nbsp;&nbsp; 没有必要在动态SQL里写事物，如果要写请写在外面如：&nbsp;&nbsp; </span><span style="color: #0000ff">begin</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">tran</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">exec</span><span style="color: #000000">(</span><span style="color: #008000">@s</span><span style="color: #000000">)&nbsp;&nbsp; </span><span style="color: #0000ff">commit</span><span style="color: #000000">&nbsp;&nbsp; trans&nbsp;&nbsp; 或者将动态SQL&nbsp;&nbsp; 写成函数或者存储过程。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">13</span><span style="color: #000000">、在查询Select语句中用Where字句限制返回的行数,避免表扫描,如果返回不必要的数据，浪费了服务器的I</span><span style="color: #808080">/</span><span style="color: #000000">O资源，加重了网络的负担降低性能。如果表很大，在表扫描的期间将表锁住，禁止其他的联接访问表,后果严重。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">14</span><span style="color: #000000">、SQL的注释申明对执行没有任何影响&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">15</span><span style="color: #000000">、尽可能不使用光标，它占用大量的资源。如果需要row</span><span style="color: #808080">-</span><span style="color: #0000ff">by</span><span style="color: #808080">-</span><span style="color: #000000">row地执行，尽量采用非光标技术,如：在客户端循环，用临时表，Table变量，用子查询，用Case语句等等。游标可以按照它所支持的提取选项进行分类：&nbsp;&nbsp; 只进&nbsp;&nbsp; 必须按照从第一行到最后一行的顺序提取行。</span><span style="color: #0000ff">FETCH</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">NEXT</span><span style="color: #000000">&nbsp;&nbsp; 是唯一允许的提取操作,也是默认方式。可滚动性&nbsp;&nbsp; 可以在游标中任何地方随机提取任意行。游标的技术在SQL2000下变得功能很强大，他的目的是支持循环。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有四个并发选项&nbsp; <br /><br />READ_ONLY：不允许通过游标定位更新(</span><span style="color: #0000ff">Update</span><span style="color: #000000">)，且在组成结果集的行中没有锁。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OPTIMISTIC&nbsp;&nbsp; </span><span style="color: #0000ff">WITH</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">valueS</span><span style="color: #000000">:乐观并发控制是事务控制理论的一个标准部分。乐观并发控制用于这样的情形，即在打开游标及更新行的间隔中，只有很小的机会让第二个用户更新某一行。当某个游标以此选项打开时，没有锁控制其中的行，这将有助于最大化其处理能力。如果用户试图修改某一行，则此行的当前值会与最后一次提取此行时获取的值进行比较。如果任何值发生改变，则服务器就会知道其他人已更新了此行，并会返回一个错误。如果值是一样的，服务器就执行修改。&nbsp;&nbsp; 选择这个并发选项OPTIMISTIC&nbsp;&nbsp; </span><span style="color: #0000ff">WITH</span><span style="color: #000000">&nbsp;&nbsp; ROW&nbsp;&nbsp; VERSIONING:此乐观并发控制选项基于行版本控制。使用行版本控制，其中的表必须具有某种版本标识符，服务器可用它来确定该行在读入游标后是否有所更改。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在&nbsp;&nbsp; SQL&nbsp;&nbsp; Server&nbsp;&nbsp; 中，这个性能由&nbsp;&nbsp; </span><span style="color: #0000ff">timestamp</span><span style="color: #000000">&nbsp;&nbsp; 数据类型提供，它是一个二进制数字，表示数据库中更改的相对顺序。每个数据库都有一个全局当前时间戳值：</span><span style="font-weight: bold; color: #008000">@@DBTS</span><span style="color: #000000">。每次以任何方式更改带有&nbsp;&nbsp; </span><span style="color: #0000ff">timestamp</span><span style="color: #000000">&nbsp;&nbsp; 列的行时，SQL&nbsp;&nbsp; Server&nbsp;&nbsp; 先在时间戳列中存储当前的&nbsp;&nbsp; </span><span style="font-weight: bold; color: #008000">@@DBTS</span><span style="color: #000000">&nbsp;&nbsp; 值，然后增加&nbsp;&nbsp; </span><span style="font-weight: bold; color: #008000">@@DBTS</span><span style="color: #000000">&nbsp;&nbsp; 的值。如果某&nbsp;&nbsp; 个表具有&nbsp;&nbsp; </span><span style="color: #0000ff">timestamp</span><span style="color: #000000">&nbsp;&nbsp; 列，则时间戳会被记到行级。服务器就可以比较某行的当前时间戳值和上次提取时所存储的时间戳值，从而确定该行是否已更新。服务器不必比较所有列的值，只需比较&nbsp;&nbsp; </span><span style="color: #0000ff">timestamp</span><span style="color: #000000">&nbsp;&nbsp; 列即可。如果应用程序对没有&nbsp;&nbsp; </span><span style="color: #0000ff">timestamp</span><span style="color: #000000">&nbsp;&nbsp; 列的表要求基于行版本控制的乐观并发，则游标默认为基于数值的乐观并发控制。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SCROLL&nbsp;&nbsp; LOCKS&nbsp;&nbsp; 这个选项实现悲观并发控制。在悲观并发控制中，在把数据库的行读入游标结果集时，应用程序将试图锁定数据库行。在使用服务器游标时，将行读入游标时会在其上放置一个更新锁。如果在事务内打开游标，则该事务更新锁将一直保持到事务被提交或回滚；当提取下一行时，将除去游标锁。如果在事务外打开游标，则提取下一行时，锁就被丢弃。因此，每当用户需要完全的悲观并发控制时，游标都应在事务内打开。更新锁将阻止任何其它任务获取更新锁或排它锁，从而阻止其它任务更新该行。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然而，更新锁并不阻止共享锁，所以它不会阻止其它任务读取行，除非第二个任务也在要求带更新锁的读取。滚动锁根据在游标定义的&nbsp;&nbsp; </span><span style="color: #0000ff">SELECT</span><span style="color: #000000">&nbsp;&nbsp; 语句中指定的锁提示，这些游标并发选项可以生成滚动锁。滚动锁在提取时在每行上获取，并保持到下次提取或者游标关闭，以先发生者为准。下次提取时，服务器为新提取中的行获取滚动锁，并释放上次提取中行的滚动锁。滚动锁独立于事务锁，并可以保持到一个提交或回滚操作之后。如果提交时关闭游标的选项为关，则&nbsp;&nbsp; </span><span style="color: #0000ff">COMMIT</span><span style="color: #000000">&nbsp;&nbsp; 语句并不关闭任何打开的游标，而且滚动锁被保留到提交之后，以维护对所提取数据的隔离。所获取滚动锁的类型取决于游标并发选项和游标&nbsp;&nbsp; </span><span style="color: #0000ff">SELECT</span><span style="color: #000000">&nbsp;&nbsp; 语句中的锁提示。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 锁提示&nbsp;&nbsp; 只读&nbsp;&nbsp; 乐观数值&nbsp;&nbsp; 乐观行版本控制&nbsp;&nbsp; 锁定无提示&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 更新&nbsp;&nbsp; NOLOCK&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 未锁定&nbsp;&nbsp; </span><span style="color: #0000ff">HOLDLOCK</span><span style="color: #000000">&nbsp;&nbsp; 共享&nbsp;&nbsp; 共享&nbsp;&nbsp; 共享&nbsp;&nbsp; 更新&nbsp;&nbsp; UPDLOCK&nbsp;&nbsp; 错误&nbsp;&nbsp; 更新&nbsp;&nbsp; 更新&nbsp;&nbsp; 更新&nbsp;&nbsp; TABLOCKX&nbsp;&nbsp; 错误&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 更新其它&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 未锁定&nbsp;&nbsp; 更新&nbsp;&nbsp; </span><span style="color: #808080">*</span><span style="color: #000000">指定&nbsp;&nbsp; NOLOCK&nbsp;&nbsp; 提示将使指定了该提示的表在游标内是只读的。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">16</span><span style="color: #000000">、用Profiler来跟踪查询，得到查询所需的时间，找出SQL的问题所在;用索引优化器优化索引&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">17</span><span style="color: #000000">、注意UNion和UNion&nbsp;&nbsp; </span><span style="color: #808080">all</span><span style="color: #000000">&nbsp;&nbsp; 的区别。</span><span style="color: #0000ff">UNION</span><span style="color: #000000">&nbsp;&nbsp; all好&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">18</span><span style="color: #000000">、注意使用DISTINCT，在没有必要时不要用，它同UNION一样会使查询变慢。重复的记录在查询里是没有问题的&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">19</span><span style="color: #000000">、查询时不要返回不需要的行、列&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">20</span><span style="color: #000000">、用sp_configure&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">query&nbsp;&nbsp; governor&nbsp;&nbsp; cost&nbsp;&nbsp; limit</span><span style="color: #ff0000">'</span><span style="color: #000000">或者SET&nbsp;&nbsp; QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源超出限制时，服务器自动取消查询,在查询之前就扼杀掉。 </span><span style="color: #0000ff">SET</span><span style="color: #000000">&nbsp;&nbsp; LOCKTIME设置锁的时间&nbsp; <br /></span><br /><span style="font-weight: bold; color: #800000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 21</span><span style="color: #000000">、用select&nbsp;&nbsp; </span><span style="color: #0000ff">top</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">100</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">/</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">10</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">Percent</span><span style="color: #000000">&nbsp;&nbsp; 来限制用户返回的行数或者SET&nbsp;&nbsp; ROWCOUNT来限制操作的行&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">22</span><span style="color: #000000">、在SQL2000以前，一般不要用如下的字句:&nbsp;&nbsp; "</span><span style="color: #0000ff">IS</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">NULL</span><span style="color: #000000">",&nbsp;&nbsp; " </span><span style="color: #808080">&lt;&gt;</span><span style="color: #000000"> ",&nbsp;&nbsp; "</span><span style="color: #808080">!=</span><span style="color: #000000">",&nbsp;&nbsp; "</span><span style="color: #808080">!&gt;</span><span style="color: #000000"> ",&nbsp;&nbsp; "! </span><span style="color: #808080">&lt;</span><span style="color: #000000">",&nbsp;&nbsp; "</span><span style="color: #808080">NOT</span><span style="color: #000000">",&nbsp;&nbsp; "</span><span style="color: #808080">NOT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">EXISTS</span><span style="color: #000000">",&nbsp;&nbsp; "</span><span style="color: #808080">NOT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">IN</span><span style="color: #000000">",&nbsp;&nbsp; "</span><span style="color: #808080">NOT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">LIKE</span><span style="color: #000000">",&nbsp;&nbsp; </span><span style="color: #808080">and</span><span style="color: #000000">&nbsp;&nbsp; "</span><span style="color: #808080">LIKE</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">%500</span><span style="color: #ff0000">'</span><span style="color: #000000">"，因为他们不走索引全是表扫描。也不要在WHere字句中的列名加函数，如Convert，substring等,如果必须用函数的时候，创建计算列再创建索引来替代.还可以变通写法：</span><span style="color: #0000ff">WHERE</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff00ff">SUBSTRING</span><span style="color: #000000">(firstname,</span><span style="font-weight: bold; color: #800000">1</span><span style="color: #000000">,</span><span style="font-weight: bold; color: #800000">1</span><span style="color: #000000">)&nbsp;&nbsp; </span><span style="color: #808080">=</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">m</span><span style="color: #ff0000">'</span><span style="color: #000000">改为WHERE&nbsp;&nbsp; firstname&nbsp;&nbsp; </span><span style="color: #808080">like</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">m%</span><span style="color: #ff0000">'</span><span style="color: #000000">（索引扫描），一定要将函数和列名分开。并且索引不能建得太多和太大。</span><span style="color: #808080">NOT</span><span style="color: #000000">&nbsp;&nbsp; IN会多次扫描表，使用EXISTS、</span><span style="color: #808080">NOT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">EXISTS</span><span style="color: #000000">&nbsp;&nbsp; ，</span><span style="color: #808080">IN</span><span style="color: #000000">&nbsp;&nbsp; ,&nbsp;&nbsp; </span><span style="color: #808080">LEFT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">OUTER</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">JOIN</span><span style="color: #000000">&nbsp;&nbsp; 来替代，特别是左连接,而Exists比IN更快，最慢的是NOT操作.如果列的值含有空，以前它的索引不起作用，现在2000的优化器能够处理了。相同的是IS&nbsp;&nbsp; </span><span style="color: #0000ff">NULL</span><span style="color: #000000">，&#8220;</span><span style="color: #808080">NOT</span><span style="color: #000000">",&nbsp;&nbsp; "</span><span style="color: #808080">NOT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">EXISTS</span><span style="color: #000000">",&nbsp;&nbsp; "</span><span style="color: #808080">NOT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">IN</span><span style="color: #000000">"能优化她，而&#8221; </span><span style="color: #808080">&lt;&gt;</span><span style="color: #000000"> &#8221;等还是不能优化，用不到索引。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">23</span><span style="color: #000000">、使用Query&nbsp;&nbsp; Analyzer，查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20</span><span style="color: #808080">%</span><span style="color: #000000">的代码占据了80</span><span style="color: #808080">%</span><span style="color: #000000">的资源，我们优化的重点是这些慢的地方。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">24</span><span style="color: #000000">、如果使用了IN或者OR等时发现查询没有走索引，使用显示申明指定索引：&nbsp;&nbsp; </span><span style="color: #0000ff">SELECT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">*</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">FROM</span><span style="color: #000000">&nbsp;&nbsp; PersonMember&nbsp;&nbsp; (</span><span style="color: #0000ff">INDEX</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">=</span><span style="color: #000000">&nbsp;&nbsp; IX_Title)&nbsp;&nbsp; </span><span style="color: #0000ff">WHERE</span><span style="color: #000000">&nbsp;&nbsp; processid&nbsp;&nbsp; </span><span style="color: #808080">IN</span><span style="color: #000000">&nbsp;&nbsp; (&#8216;男&#8217;，&#8216;女&#8217;)&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">25</span><span style="color: #000000">、将需要查询的结果预先计算好放在表中，查询的时候再SELECT。这在SQL7.0以前是最重要的手段。例如医院的住院费计算。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">26</span><span style="color: #000000">、</span><span style="color: #ff00ff">MIN</span><span style="color: #000000">()&nbsp;&nbsp; 和&nbsp;&nbsp; </span><span style="color: #ff00ff">MAX</span><span style="color: #000000">()能使用到合适的索引&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">27</span><span style="color: #000000">、数据库有一个原则是代码离数据越近越好，所以优先选择Default,依次为Rules,Triggers,&nbsp;&nbsp; </span><span style="color: #0000ff">Constraint</span><span style="color: #000000">（约束如外健主健CheckUNIQUE&#8230;&#8230;,数据类型的最大长度等等都是约束）,</span><span style="color: #0000ff">Procedure</span><span style="color: #000000">.这样不仅维护工作小，编写程序质量高，并且执行的速度快。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">28</span><span style="color: #000000">、如果要插入大的二进制值到Image列，使用存储过程，千万不要用内嵌INsert来插入(不知JAVA是否)。因为这样应用程序首先将二进制值转换成字符串（尺寸是它的两倍），服务器受到字符后又将他转换成二进制值.存储过程就没有这些动作:&nbsp;&nbsp; 方法：</span><span style="color: #0000ff">Create</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">procedure</span><span style="color: #000000">&nbsp;&nbsp; p_insert&nbsp;&nbsp; </span><span style="color: #0000ff">as</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">insert</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">into</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">table</span><span style="color: #000000">(Fimage)&nbsp;&nbsp; </span><span style="color: #0000ff">values</span><span style="color: #000000">&nbsp;&nbsp; (</span><span style="color: #008000">@image</span><span style="color: #000000">),&nbsp;&nbsp; 在前台调用这个存储过程传入二进制参数，这样处理速度明显改善。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">29</span><span style="color: #000000">、Between在某些时候比IN速度更快,Between能够更快地根据索引找到范围。用查询优化器可见到差别。&nbsp;&nbsp; </span><span style="color: #0000ff">select</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">*</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">from</span><span style="color: #000000">&nbsp;&nbsp; chineseresume&nbsp;&nbsp; </span><span style="color: #0000ff">where</span><span style="color: #000000">&nbsp;&nbsp; title&nbsp;&nbsp; </span><span style="color: #808080">in</span><span style="color: #000000">&nbsp;&nbsp; (</span><span style="color: #ff0000">'</span><span style="color: #ff0000">男</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">女</span><span style="color: #ff0000">'</span><span style="color: #000000">)&nbsp;&nbsp; </span><span style="color: #0000ff">Select</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">*</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">from</span><span style="color: #000000">&nbsp;&nbsp; chineseresume&nbsp;&nbsp; </span><span style="color: #0000ff">where</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">between</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">男</span><span style="color: #ff0000">'</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">and</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">女</span><span style="color: #ff0000">'</span><span style="color: #000000">&nbsp;&nbsp; 是一样的。由于in会在比较多次，所以有时会慢些。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">30</span><span style="color: #000000">、在必要是对全局或者局部临时表创建索引，有时能够提高速度，但不是一定会这样，因为索引也耗费大量的资源。他的创建同是实际表一样。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">31</span><span style="color: #000000">、不要建没有作用的事物例如产生报表时，浪费资源。只有在必要使用事物时使用它。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">32</span><span style="color: #000000">、用OR的字句可以分解成多个查询，并且通过UNION&nbsp;&nbsp; 连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引，用UNION&nbsp;&nbsp; all执行的效率更高.多个OR的字句没有用到索引，改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">33</span><span style="color: #000000">、尽量少用视图，它的效率低。对视图操作比直接对表操作慢,可以用stored&nbsp;&nbsp; procedure来代替她。特别的是不要用视图嵌套,嵌套视图增加了寻找原始资料的难度。我们看视图的本质：它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时，不要使用指向多个表的视图，直接从表检索或者仅仅包含这个表的视图上读，否则增加了不必要的开销,查询受到干扰.为了加快视图的查询，MsSQL增加了视图索引的功能。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">34</span><span style="color: #000000">、没有必要时不要用DISTINCT和ORDER&nbsp;&nbsp; </span><span style="color: #0000ff">BY</span><span style="color: #000000">，这些动作可以改在客户端执行。它们增加了额外的开销。这同UNION&nbsp;&nbsp; 和UNION&nbsp;&nbsp; ALL一样的道理。&nbsp;&nbsp; </span><span style="color: #0000ff">SELECT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">top</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">20</span><span style="color: #000000">&nbsp;&nbsp; ad.companyname,comid,position,ad.referenceid,worklocation,&nbsp;&nbsp; </span><span style="color: #ff00ff">convert</span><span style="color: #000000">(</span><span style="color: #0000ff">varchar</span><span style="color: #000000">(</span><span style="font-weight: bold; color: #800000">10</span><span style="color: #000000">),ad.postDate,</span><span style="font-weight: bold; color: #800000">120</span><span style="color: #000000">)&nbsp;&nbsp; </span><span style="color: #0000ff">as</span><span style="color: #000000">&nbsp;&nbsp; postDate1,workyear,degreedescription&nbsp;&nbsp; </span><span style="color: #0000ff">FROM</span><span style="color: #000000">&nbsp;&nbsp; jobcn_query.dbo.COMPANYAD_query&nbsp;&nbsp; ad&nbsp;&nbsp; </span><span style="color: #0000ff">where</span><span style="color: #000000">&nbsp;&nbsp; referenceID&nbsp;&nbsp; </span><span style="color: #808080">in</span><span style="color: #000000">(</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00329667</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD132168</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00337748</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00338345</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00333138</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00303570</span><span style="color: #ff0000">'</span><span style="color: #000000">,&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00303569</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00303568</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00306698</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00231935</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00231933</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00254567</span><span style="color: #ff0000">'</span><span style="color: #000000">,&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00254585</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00254608</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00254607</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00258524</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00332133</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00268618</span><span style="color: #ff0000">'</span><span style="color: #000000">,&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00279196</span><span style="color: #ff0000">'</span><span style="color: #000000">,</span><span style="color: #ff0000">'</span><span style="color: #ff0000">JCNAD00268613</span><span style="color: #ff0000">'</span><span style="color: #000000">)&nbsp;&nbsp; </span><span style="color: #0000ff">order</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">by</span><span style="color: #000000">&nbsp;&nbsp; postdate&nbsp;&nbsp; </span><span style="color: #0000ff">desc</span><span style="color: #000000">&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">35</span><span style="color: #000000">、在IN后面值的列表中，将出现最频繁的值放在最前面，出现得最少的放在最后面，减少判断的次数&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">36</span><span style="color: #000000">、当用SELECT&nbsp;&nbsp; INTO时，它会锁住系统表(sysobjects，sysindexes等等)，阻塞其他的连接的存取。创建临时表时用显示申明语句，而不是 </span><span style="color: #0000ff">select</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">INTO</span><span style="color: #000000">.&nbsp;&nbsp; </span><span style="color: #0000ff">drop</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">table</span><span style="color: #000000">&nbsp;&nbsp; t_lxh&nbsp;&nbsp; </span><span style="color: #0000ff">begin</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">tran</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">select</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">*</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">into</span><span style="color: #000000">&nbsp;&nbsp; t_lxh&nbsp;&nbsp; </span><span style="color: #0000ff">from</span><span style="color: #000000">&nbsp;&nbsp; chineseresume&nbsp;&nbsp; </span><span style="color: #0000ff">where</span><span style="color: #000000">&nbsp;&nbsp; name&nbsp;&nbsp; </span><span style="color: #808080">=</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff0000">'</span><span style="color: #ff0000">XYZ</span><span style="color: #ff0000">'</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #008080">--</span><span style="color: #008080">commit&nbsp;&nbsp; 在另一个连接中SELECT&nbsp;&nbsp; *&nbsp;&nbsp; from&nbsp;&nbsp; sysobjects可以看到&nbsp;&nbsp; SELECT&nbsp;&nbsp; INTO&nbsp;&nbsp; 会锁住系统表，Create&nbsp;&nbsp; table&nbsp;&nbsp; 也会锁系统表(不管是临时表还是系统表)。所以千万不要在事物内使用它！！！这样的话如果是经常要用的临时表请使用实表，或者临时表变量。&nbsp; </span><span style="color: #008080"><br /></span><span style="color: #000000"><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">37</span><span style="color: #000000">、一般在GROUP&nbsp;&nbsp; </span><span style="color: #0000ff">BY</span><span style="color: #000000">&nbsp;&nbsp; 个HAVING字句之前就能剔除多余的行，所以尽量不要用它们来做剔除行的工作。他们的执行顺序应该如下最优：</span><span style="color: #0000ff">select</span><span style="color: #000000">&nbsp;&nbsp; 的Where字句选择所有合适的行，</span><span style="color: #0000ff">Group</span><span style="color: #000000">&nbsp;&nbsp; By用来分组个统计行，Having字句用来剔除多余的分组。这样Group&nbsp;&nbsp; </span><span style="color: #0000ff">By</span><span style="color: #000000">&nbsp;&nbsp; 个Having的开销小，查询快.对于大的数据行进行分组和Having十分消耗资源。如果Group&nbsp;&nbsp; BY的目的不包括计算，只是分组，那么用Distinct更快&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">38</span><span style="color: #000000">、一次更新多条记录比分多次更新每次一条快,就是说批处理好&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">39</span><span style="color: #000000">、少用临时表，尽量用结果集和Table类性的变量来代替它,</span><span style="color: #0000ff">Table</span><span style="color: #000000">&nbsp;&nbsp; 类型的变量比临时表好&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">40</span><span style="color: #000000">、在SQL2000下，计算字段是可以索引的，需要满足的条件如下：&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a、计算字段的表达是确定的&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b、不能用在TEXT,</span><span style="color: #0000ff">Ntext</span><span style="color: #000000">，Image数据类型&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c、必须配制如下选项&nbsp;&nbsp; ANSI_NULLS&nbsp;&nbsp; </span><span style="color: #808080">=</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">ON</span><span style="color: #000000">,&nbsp;&nbsp; ANSI_PADDINGS&nbsp;&nbsp; </span><span style="color: #808080">=</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">ON</span><span style="color: #000000">,&nbsp;&nbsp; &#8230;&#8230;.&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">41</span><span style="color: #000000">、尽量将数据的处理工作放在服务器上，减少网络的开销，如使用存储过程。存储过程是编译好、优化过、并且被组织到一个执行规划里、且存储在数据库中的 SQL语句，是控制流语言的集合，速度当然快。反复执行的动态SQL,可以使用临时存储过程，该过程（临时表）被放在Tempdb中。以前由于SQL&nbsp;&nbsp; SERVER对复杂的数学计算不支持，所以不得不将这个工作放在其他的层上而增加网络的开销。SQL2000支持UDFs,现在支持复杂的数学计算，函数的返回值不要太大，这样的开销很大。用户自定义函数象光标一样执行的消耗大量的资源，如果返回大的结果采用存储过程&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">42</span><span style="color: #000000">、不要在一句话里再三的使用相同的函数，浪费资源,将结果放在变量里再调用更快&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">43</span><span style="color: #000000">、</span><span style="color: #0000ff">SELECT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff00ff">COUNT</span><span style="color: #000000">(</span><span style="color: #808080">*</span><span style="color: #000000">)的效率教低，尽量变通他的写法，而EXISTS快.同时请注意区别：&nbsp;&nbsp; </span><span style="color: #0000ff">select</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff00ff">count</span><span style="color: #000000">(Field&nbsp;&nbsp; </span><span style="color: #0000ff">of</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">null</span><span style="color: #000000">)&nbsp;&nbsp; </span><span style="color: #0000ff">from</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">Table</span><span style="color: #000000">&nbsp;&nbsp; 和&nbsp;&nbsp; </span><span style="color: #0000ff">select</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff00ff">count</span><span style="color: #000000">(Field&nbsp;&nbsp; </span><span style="color: #0000ff">of</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #808080">NOT</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">null</span><span style="color: #000000">)&nbsp;&nbsp; </span><span style="color: #0000ff">from</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #0000ff">Table</span><span style="color: #000000">&nbsp;&nbsp; 的返回值是不同的。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">44</span><span style="color: #000000">、当服务器的内存够多时，配制线程数量&nbsp;&nbsp; </span><span style="color: #808080">=</span><span style="color: #000000">&nbsp;&nbsp; 最大连接数</span><span style="color: #808080">+</span><span style="font-weight: bold; color: #800000">5</span><span style="color: #000000">，这样能发挥最大的效率；否则使用&nbsp;&nbsp; 配制线程数量 </span><span style="color: #808080">&lt;</span><span style="color: #000000">最大连接数启用SQL&nbsp;&nbsp; SERVER的线程池来解决,如果还是数量&nbsp;&nbsp; </span><span style="color: #808080">=</span><span style="color: #000000">&nbsp;&nbsp; 最大连接数</span><span style="color: #808080">+</span><span style="font-weight: bold; color: #800000">5</span><span style="color: #000000">，严重的损害服务器的性能。&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">45</span><span style="color: #000000">、按照一定的次序来访问你的表。如果你先锁住表A，再锁住表B，那么在所有的存储过程中都要按照这个顺序来锁定它们。如果你（不经意的）某个存储过程中先锁定表B，再锁定表A，这可能就会导致一个死锁。如果锁定顺序没有被预先详细的设计好，死锁很难被发现&nbsp; <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">46</span><span style="color: #000000">、通过SQL&nbsp;&nbsp; Server&nbsp;&nbsp; Performance&nbsp;&nbsp; Monitor监视相应硬件的负载&nbsp;&nbsp; Memory:&nbsp;&nbsp; Page&nbsp;&nbsp; Faults&nbsp;&nbsp; </span><span style="color: #808080">/</span><span style="color: #000000">&nbsp;&nbsp; sec计数器如果该值偶尔走高，表明当时有线程竞争内存。如果持续很高，则内存可能是瓶颈。&nbsp;&nbsp; Process:&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">1)</span><span style="color: #000000">、</span><span style="color: #808080">%</span><span style="color: #000000">&nbsp;&nbsp; DPC&nbsp;&nbsp; Time&nbsp;&nbsp; 指在范例间隔期间处理器用在缓延程序调用(DPC)接收和提供服务的百分比。(DPC&nbsp;&nbsp; 正在运行的为比标准间隔优先权低的间隔)。&nbsp;&nbsp; 由于&nbsp;&nbsp; DPC&nbsp;&nbsp; 是以特权模式执行的，DPC&nbsp;&nbsp; 时间的百分比为特权时间&nbsp;&nbsp; 百分比的一部分。这些时间单独计算并且不属于间隔计算总数的一部&nbsp;&nbsp; 分。这个总数显示了作为实例时间百分比的平均忙时。&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">2)</span><span style="color: #000000">、</span><span style="color: #808080">%</span><span style="color: #000000">Processor&nbsp;&nbsp; Time计数器　如果该参数值持续超过95</span><span style="color: #808080">%</span><span style="color: #000000">，表明瓶颈是CPU。可以考虑增加一个处理器或换一个更快的处理器。&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">3)</span><span style="color: #000000">、</span><span style="color: #808080">%</span><span style="color: #000000">&nbsp;&nbsp; Privileged&nbsp;&nbsp; Time&nbsp;&nbsp; 指非闲置处理器时间用于特权模式的百分比。(特权模式是为操作系统组件和操纵硬件驱动程序而设计的一种处理模式。它允许直接访问硬件和所有内存。另一种模式为用户模式，它是一种为应用程序、环境分系统和整数分系统设计的一种有限处理模式。操作系统将应用程序线程转换成特权模式以访问操作系统服务)。&nbsp;&nbsp; 特权时间的&nbsp;&nbsp; </span><span style="color: #808080">%</span><span style="color: #000000">&nbsp;&nbsp; 包括为间断和&nbsp;&nbsp; DPC&nbsp;&nbsp; 提供服务的时间。特权时间比率高可能是由于失败设备产生的大数量的间隔而引起的。这个计数器将平均忙时作为样本时间的一部分显示。&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold; color: #800000">(</span></span><span style="font-weight: bold; color: #800000">4)</span><span style="color: #000000">、</span><span style="color: #808080">%</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="color: #ff00ff">User</span><span style="color: #000000">&nbsp;&nbsp; Time表示耗费CPU的数据库操作，如排序，执行aggregate&nbsp;&nbsp; functions等。如果该值很高，可考虑增加索引，尽量使用简单的表联接，水平分割大表格等方法来降低该值。&nbsp;&nbsp; Physical&nbsp;&nbsp; </span><span style="color: #0000ff">Disk</span><span style="color: #000000">:&nbsp;&nbsp; Curretn&nbsp;&nbsp; </span><span style="color: #0000ff">Disk</span><span style="color: #000000">&nbsp;&nbsp; Queue&nbsp;&nbsp; Length计数器该值应不超过磁盘数的1.</span><span style="font-weight: bold; color: #800000">5</span><span style="color: #808080">~</span><span style="color: #000000">2倍。要提高性能，可增加磁盘。&nbsp;&nbsp; SQLServer:Cache&nbsp;&nbsp; Hit&nbsp;&nbsp; Ratio计数器该值越高越好。如果持续低于80</span><span style="color: #808080">%</span><span style="color: #000000">，应考虑增加内存。&nbsp;&nbsp; 注意该参数值是从SQL&nbsp;&nbsp; Server启动后，就一直累加记数，所以运行经过一段时间后，该值将不能反映系统当前值。&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">47</span><span style="color: #000000">、分析select&nbsp;&nbsp; emp_name&nbsp;&nbsp; form&nbsp;&nbsp; employee&nbsp;&nbsp; </span><span style="color: #0000ff">where</span><span style="color: #000000">&nbsp;&nbsp; salary&nbsp;&nbsp; </span><span style="color: #808080">&gt;</span><span style="color: #000000">&nbsp;&nbsp; </span><span style="font-weight: bold; color: #800000">3000</span><span style="color: #000000">&nbsp;&nbsp; 在此语句中若salary是Float类型的，则优化器对其进行优化为Convert(</span><span style="color: #0000ff">float</span><span style="color: #000000">,</span><span style="font-weight: bold; color: #800000">3000</span><span style="color: #000000">)，因为3000是个整数，我们应在编程时使用3000.0而不要等运行时让DBMS进行转化。同样字符和整型数据的转换。</span></p><!--End_rbody_51018973//--></div><img src ="http://www.blogjava.net/AndyZhang/aggbug/356874.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/AndyZhang/" target="_blank">SkyDream</a> 2011-08-19 16:54 <a href="http://www.blogjava.net/AndyZhang/archive/2011/08/19/356874.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>