<?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-weibogao</title><link>http://www.blogjava.net/weibogao/</link><description>my second lifespace</description><language>zh-cn</language><lastBuildDate>Sat, 11 Oct 2008 08:30:44 GMT</lastBuildDate><pubDate>Sat, 11 Oct 2008 08:30:44 GMT</pubDate><ttl>60</ttl><item><title>用CVS来管理自己的程序</title><link>http://www.blogjava.net/weibogao/archive/2007/01/16/94195.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Tue, 16 Jan 2007 04:49:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/16/94195.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/94195.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/16/94195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/94195.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/94195.html</trackback:ping><description><![CDATA[
		<a href="http://51cmm.csai.cn/casepanel/CM/No047.htm">
				<font color="#1d58d1">http://51cmm.csai.cn/casepanel/CM/No047.htm</font>
		</a>
		<br />
		<p>每个编写程序的人也许都有过这样的体验：对编写的程序作了一些修改，几天以后，我们可能发现上次的修改导致了其它的矛盾，甚至编译不能通过，但这时我们却很难找到自己刚在哪里作了改动。一般说来，我们总是希望自己完整的记录下一个程序开发的过程，记录下这个程序的每一点改进和调整。或许我们可以用备份的方法来解决这个问题，于是我们建立一个backup的目录，定期或不定期的将自己的源程序打包放进去，直到某一天整个硬盘都被这些文件撑满。这当然有些夸张，但这种机制带给我们的不方便是显而易见的。更进一步，现在一个软件产品的开发，一个人单枪匹马很难完成。可能是包含几个、几十个乃至上百个程序员协作开发，这时的源代码又该如何管理？解决问题的方法就是使用CVS。 <br />　　CVS - Concurrent Versions System(并发版本管理系统)是一个版本控制管理系统，它是目前最为广泛使用的一个系统。从gftp到gtk到KDE，你几乎可以在每一个你熟悉的自由软件的源码里看到它的踪迹(下面我们会知道，它的踪迹指一个称为cvs的子目录)。同样，你也可以在几乎每一个Linux的发行版本里看到CVS系统。可以说，如果失去了CVS，现有的许多多人协作、自由开发的软件都会在一定程度上放慢自己发展的步伐。 　　CVS到底有哪些功能，使得它有如此强大的魅力呢？<br /><strong>　　1.CVS能做什么？</strong><br />　　　如上所说，CVS首先是一个版本管理系统，它可以保留软件开发过程中的每一个版本的信息，包括谁、在何时、作了什么样的修改以及为什么作这样的修改等。这个功能和以前流行于Linux和Unix的版本管理系统RCS(Revision Control System)和SCCS(Source Code Control System)很象。但CVS的功能远非仅此。它的最大的特点是它的并发性，即它支持分布式项目的开发。在互联网席卷一切的今天，这个功能太为重要了。小到一个办公室内部开发一个OA系统，大到KDE小组利用互联网开发新版本的KDE，CVS都可以一展身手。一个程序员开发出了自己负责模块的新版本后，迅速的通过CVS让开发组的每一个成员都分享自己的最新成果。甚至，CVS通过特定的机制允许多个程序员同时修改同一个源程序文件。<br />　　另外CVS增强的目录结构以及对二进制文件良好的处理，都使得它远远优于其它的版本管理系统。最后，必须一提的是CVS是基于RCS开发而成的。<br /><strong>　　2.如何得到CVS?<br /></strong>　　　CVS在几乎包含在所有的Linux发布版本中，如RedHat、Turbo Linux、Slackware以及国产的红旗、Xteam Linux等。你可以试着敲一下cvs命令，大多数情况下都会出现以Usage: cvs开头的一堆信息，提示你如何使用cvs，这意味着在你的机器上早已有了CVS，只是遗憾的是它一直未被你发现和利用。运气不好的话，你会看到形如cvs: Command not found.的提示，这意味着你的机器没有安装CVS。这时你有两种选择。一是找到你的Linux安装盘，从那里安装CVS。例如在使用RPM方式安装的Linux(上面介绍的几个发布版本中似乎除了Slackware，其他都是)发布版本中，找到cvs***.rpm，用rpm命令进行安装。第二种方式是到一些站点cvs的源代码，然后遵循里面附带的指导进行安装，需要的读者请访问站点www.cyclic.com/或者http://www.loria.fr/~molli/cvs-index.html。<br /><strong>　　3.CVS的基本使用方法<br /></strong>　　　在这一节里，我们来学习掌握CVS的一些基本使用方法。现在我们假定已经安装好了CVS，并且我们打算用它来管理自己正在开发的一个软件，软件名叫netants，它存放在硬盘上一个叫做netants的目录里，目前里面有了文件netants.c、netants.h、http.c、http.h和Makefile。我们并没有和它人协作开发这个软件，也没有利用Internet或者Intranet来开发这个软件。或许，它只是自己的一个业余作品，试图写出一个比Windows下的网络蚂蚁更好的下载工具出来。<br /><strong>　　3.1初始化CVS　<br /></strong>　　我们首先要使用的命令是cvs init，这个命令用来初始化CVS系统。正如我们所看到的，所有的CVS命令都以cvs开头，然后在后面紧跟命令、参数和一些选项。初始化CVS系统主要是为了创建一个为CVS所使用的源码储存库(repository)。创建的时候，需要指定在那个目录下创建这个源码储存库。有两种方法来指定目录。一是利用"-d "选项来指定，例如：-d /usr/local/cvsroot。另一种更方便的方法是在shell里设定一个名叫CVSROOT的环境变量。使用csh或者tcsh的用户可以使用命令setenv来设定，在文件.csh rc或者文件.tcshrc里添加入下的一行：　setenv CVSROOT /usr/local/cvsroot　使用sh或者bash的用户需要在文件.profile或者文件.bashrc里添加如下两行：　CVSROOT=/usr/local/cvsroot　export CVSROOT　设置了环境变量CVSROOT后，我们运行命令cvs init，CVS将在指定的目录下面建立自己所需要的一些文件，以后我们使用CVS管理的任何项目，都会被CVS储存在这个目录之下。不过千万要注意的是：永远不要去试图修改这个目录下的文件。这个目录是由CVS自己进行管理的，轻率的改动可能会导致你丢失你部分或全部的交由CVS管理的源代码或其他资源。<br /><strong>　　3.2导入项目到CVS中去<br /></strong>　　初始化结束以后，我们就要真正开始利用CVS来管理自己的程序网络蚂蚁了。第一步，我们将这个项目交由CVS管理。使用如下的CVS的import命令，将源程序导入到CVS的源码储存库中去：　cd netants　cvs import -m "start my project: Netants" netants yoyo start　这个命令看起来有些复杂，需要解释一下。import是cvs的导入命令，默认状况下，它循环的将当前目录下的所有文件(包括子目录)导入到源码库(即CVSROOT指定的目录)里去。-m "start my project: Netants"告诉CVS你对这一步操作的说明。这是CVS强制要求的，如果你没有使用这种-m "字符串"的选项，CVS将会弹出一个文本编辑器(如果自己不特别指定的话，在Linux下一般是vi，而在Windows下则是Notepad)，让你输入一些说明信息它才罢休。netants是这个项目被CVS存储时的路径名，即CVS将在创建一个$CVSROOT/netants的目录，并在此目录下存放此项目的文件，当然，它不是原封不动的存储，CVS会做一番处理。最后两个字符串设定了两个标记(tag)，现在并没有什么用处，但它们同样是CVS指定必需的，所以我们添上这两个参数。执行此命令时，CVS自动将所有的文件版本设为1.1，这是它所认为的最低版本。以下为执行上述命令后的显示信息：N netants/netants.c N netants/http.c N netants/http.h N netants/netants.h N netants/Makefile No conflicts created by this import N表示New，CVS成功的加载了这些文件，并没有发现冲突。　上面的命令稍长了一些，而且显得有些繁琐，相信我，CVS不总是这样的，这点"繁琐"相对它给我们带来的便利是完全可以忽略不计的。<br /><strong>　　3.3从CVS中导出项目　<br /></strong>　　好了，我们把自己的netants的项目交给了CVS去管理，现在，我们完全可以删除原有的存储我们代码的netants目录(当然，安全起见，你或许应该再做一次备份，并希望是最后一次)。我们要进行开发工作了，建一个目录，叫什么呢，就叫worktmp吧。我们进到此目录下，执行命令cvs checkout netants，我们将会看到如下的信息：cvs checkout: Updating netants U netants/Makefile U netants/http.c U netants/http.h U netants/netants.c U netants/netants.h　CVS在当前目录下建立一个叫做netants的目录，我们原先的代码文件都在这个目录下出现了，而且还多了一个名为CVS的目录。目录CVS下面存放的是一些文本文件，记录了CVSROOT的位置、此项目对应源码库中那个目录等一些信息。<br /><strong>　　3.4保存修改到CVS中<br /></strong>　　　现在，我们开始艰苦卓越的编程工作。经过数十分钟、数小时乃至数天的工作，我们对原有的代码做了较大的修改，现在要告一段落了。我们将修改的内容提交给CVS，于是，我们需要执行命令　cvs commit -m "Made some useful changes on some files"　这时，我们将会看到CVS给出一些提示信息，它扫描并比较此目录下的现有文件和它在源码库中保存的原有文件，做了修改的文件将被更新，并且有了新的版本号：1.2。-m参数如同前面所说，是为了不想它启动一个文本编辑器来让自己输入。如果我们仅是修改了其中一两个文件，我们可以在上面的命令的最后附上文件名，这样CVS只会比较、更新指定的文件。注意的是，和自己做备份不同，CVS只是保存了不同版本之间的差异，并没有完整的保存各个版本。现在，你是不是觉得CVS有点用处了。<br /><strong>　　3.5添加文件到项目中<br /></strong>　　有一天，我们开始考虑给我们的网络蚂蚁加上从ftp站点下载文件的功能，于是，我们需要在原有的项目里添加两个文件：ftp.c和ftp.h。首先，我们在工作目录下建立并编辑、修改、生成了这两个文件，然后我们使用命令add命令来添加。　cvs add ftp.c ftp.h　此时，文件并没有真正的被添加，只是相当于"注册"了一下，要使这个过程生效，我们仍然需要使用commit命令：　cvs commit ftp.c ftp.h -m "Add two files: ftp.c and ftp.h"　此时，CVS将把这两个文件添加到项目中去，他们的版本均为初始的1.1。　　<strong>3.6从项目中删除文件</strong><br />　　除了添加以外，我们有的时候可能需要删除某个文件，例如我们发现文件netants.h其实没有什么用。于是，我们执行下面几个命令来完成删除工作：　rm netants.h　cvs remove netants.h　cvs commit netants.h -m "Delete a file."　要注意的是，CVS只是删除了当前版本的netants.h，它以前的版本依然存在，除非它恰好仅有1.1版本。<br /><strong>　　3.7设定特定版本号　<br /></strong>　　经过一段时间的工作，程序已经初具规模，形成了较稳定的版本。这个时候，netants.c可能已经是5.4版本，而http.c可能是3.5版本，而我们希望将当前的代码作一个版本发布。此时，我们需要使用的是tag命令。这个命令赋予指定的一个或多个文件一个给定的文本形式的版本号。版本号必须以字母开始，可以包含数字、下划线和连接符号(-)。我们想给当前项目的所有文件赋予相同的版本号时，可以不指定文件或路径参数，CVS默认选择当前目录下所有在CVS中注册的文件(循环进子目录)。下面既是一个例子：　键入命令：cvs tag release0-1　提示信息：　　　　cvs tag: Tagging .　　　　T Makefile　　　　T ftp.c　　　　T ftp.h　　　　T http.c　　　　T http.h　　　　T netants.c　这样当前版本的所有文件都有了一个叫做release0-1的版本代号。当我们需要这个版本的时候，我们使用-r (版本代号)参数来得到指定的版本。例如命令：　cvs checkout -r release0-1 netants　将在当前目录下建立netants目录，并导出所有版本代号为release0-1的文件。<br /><strong>　　3.8更新当前工作目录中的文件<br /></strong>　　　这里使用的命令为update，它将比较指定的在CVS源码库中的文件和当前目录下的文件，如果CVS源码库中有更高版本的源文件，则更新当前目录下的文件。这个功能主要是多人协作开发项目时使用的，让你及时分享同伴的工作成果。但它另外一个重要的用途，同样适用于单人开发的项目。这个用途需要使用-j参数，我们看下面的例子：　cvs update -j 1.5 -j 1.3 netants.c　这个命令的功能是，在当前目录的netants.c文件中，忽略从版本1.3到版本1.5所作的修改。毫无疑问，对程序员来说，这是一个非常重要的功能。因为在某个阶段我们对程序所作的修改在现在可能会被视为是无效乃至错误的，这个功能很好的解决了这个问题。　在更新的过程中，CVS执行一个自动合并的过程。例如我们的工作目录中的netants.c文件版本是2.1，并且我们已经对此文件作了一番修改，而CVS源码库中的是版本2.2，此时我们执行update命令时，CVS并不是简单的将版本2.2覆盖版本2.1，而是试图将自版本2.1到版本2.2的修改添加到当前目录中的文件中去，如果它和我们刚刚所作的修改有冲突，则CVS会以字符串"&gt;&gt;&gt;&gt;"表示由冲突发生，期待用户去修改。CVS拒绝接受包含有上述特定字符串的文件。下面即是一个冲突的例子：netants.c:版本号2.2，保存在CVS中……getPartFile( ); showFinished(); return(A); }……<br />　　netants:版本号2.1经过我们的修改……getPartFile( ); return(B); }……我们执行命令cvs update netants.c后，将会包含如下内容的新的netants.c：<br />　　……getPartFile( ); showFinished(); &gt;&gt;&gt;&gt;&gt;&gt; 2.2 }……　除非我们做出修改并删去"&gt;&gt;&gt;&gt;&gt;&gt;"，否则在执行cvs commit的时候，netants.c将不会更新原有的2.2版本。<br /><strong>　　4.CVS的其他功能<br /></strong>　　CVS当然远不止上面所说的这些内容，这些仅是CVS的基本功能，CVS还有许多重要的功能，如上面所说的网络工作方式、支持二进制文件等。下面我们对这些功能作简单的说明。<br /><strong>　　4.1 CVS的网络工作方式　<br /></strong>　　CVS的网络功能采用client-server结构，两地均需安装CVS。CVS采用rsh方式或者口令校验方式进行工作。对client端，同前面讲过的设置环境变量CVSROOT一样，用户需要设置新的环境变量CVS_SERVER，指明CVS在server上的路径，例如：/usr/local/cvsroot1。CVS的-d参数指定路径名，它后面可以用：(local或server或ext)：来指明是在本地还是在异地服务器上，默认当然是在本地，正如我们在初始化CVS一节所使用的那样。下面的命令假定我们的CVS服务器为cvs.rdcps.ac.cn，用户名为crazyyao，CVS源码库在服务器的/usr/local/cvsroot1目录下，我们的工作项目还是netants，我们用rsh方式导出项目文件：　cvs -d : server : crazyyao@cvs.rdcps.ac.cn :/usr/local/cvsroot1 checkout netants　采用口令校验方式时，需要对修改系统文件/etc/inetd.conf，以便使inetd知道如何分配、处理CVS Server的请求和响应。CVS会在源码库所在的目录中创建一个名为passwd的口令文件，对用户进行校验。使用口令校验时，CVS支持匿名登陆，而且CVS项目超级用户可以设置项目中文件的存取权限。　关于如何配置CVS使之工作在网络方式下的详细信息请参考CVS的文档。<br /><strong>　　4.2 CVS的分支和融合功能<br /></strong>　　　CVS增强的目录工作方式使得CVS提供分支和融合功能。有的时候，当项目进展到一定程度时，可能需要暂时中断，去做另外一些修改和发展。例如，我们的软件原有版本为1.0，并已提交用户使用，现在正在开发2.0。某一天，1.0的用户发现了一个较大的bug或者需要添加某个短小的功能，这时我们不能让用户去期待2.0版本，又必须给用户满意的答复，比较理想的解决方式是把现在的工作先放到一边，另开一个分支，去满足用户的需要。当此分支完成后，程序源还可以使用CVS的融合功能将这一部分修改添加到我们开发2.0版本的主工作进程中去。　创建分支可以使用tag -b命令。例如下面的命令　　　cvs tag -b netants-1-0-patch　在当前的工作目录的基础上创建一个叫做netant-1-0-patch的分支。　融合的命令参数是-j，我们在前面已经提及它了。<br /><strong>　　4.3 CVS处理二进制文件的功能　<br /></strong>　　CVS可以保存二进制文件，但和文本文件相比，它的许多功能丧失了。对于文本文件，CVS可以辨别出文件的任何一点改动，但对于二进制文件它无能为力。但是，CVS可以区分出文件作了改动，并会提示用户自己修改、保存。与文本文件不同，CVS保存二进制文件每个版本的完整信息。在操作二进制文件时，需要添加参数-KB，以便告诉CVS不把它当作文本文件看待。<br /><strong>　　4.4 CVS比较文件的功能　<br /></strong>　　执行的命令为diff，这个功能和shell下的diff功能基本一样。例如下面的命令比较CVS源码库中的最新的netants.c文件和当前目录下netants.c文件有什么不同：　cvs diff netatns.c<br /><strong>　　5.结束语<br /></strong>　　通过上面的介绍，希望能激起大家使用CVS的兴趣，并掌握使用CVS的一些基本方法。碰到困难时，别忘了翻阅CVS附带的手册，不过，它有厚厚的172页。希望CVS能加速你的软件开发。</p>
		<br />
<img src ="http://www.blogjava.net/weibogao/aggbug/94195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-16 12:49 <a href="http://www.blogjava.net/weibogao/archive/2007/01/16/94195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCVS与CVSNT简明使用手则(二)</title><link>http://www.blogjava.net/weibogao/archive/2007/01/16/94192.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Tue, 16 Jan 2007 04:43:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/16/94192.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/94192.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/16/94192.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/94192.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/94192.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 5.2    						管理员进行管理演练：														1.         						登陆远程						CVSNT						：														l         						选择						Admin=&gt;Login						菜单，默认设置，						OK						。								...&nbsp;&nbsp;<a href='http://www.blogjava.net/weibogao/archive/2007/01/16/94192.html'>阅读全文</a><img src ="http://www.blogjava.net/weibogao/aggbug/94192.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-16 12:43 <a href="http://www.blogjava.net/weibogao/archive/2007/01/16/94192.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCVS与CVSNT简明使用手则(一)</title><link>http://www.blogjava.net/weibogao/archive/2007/01/16/94191.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Tue, 16 Jan 2007 04:42:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/16/94191.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/94191.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/16/94191.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/94191.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/94191.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1       						前言：														CVS						是版本控制的利器，目前在						Linux						和						Windows						下都有不同版本；但是国内大多数应用介绍都是基于						Linux						等开放源代码的开放性软件组织，而且讲解的也不系统，让人摸不着头脑；						Windows...&nbsp;&nbsp;<a href='http://www.blogjava.net/weibogao/archive/2007/01/16/94191.html'>阅读全文</a><img src ="http://www.blogjava.net/weibogao/aggbug/94191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-16 12:42 <a href="http://www.blogjava.net/weibogao/archive/2007/01/16/94191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《CVSNT用户管理方案》转载</title><link>http://www.blogjava.net/weibogao/archive/2007/01/16/94190.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Tue, 16 Jan 2007 04:38:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/16/94190.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/94190.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/16/94190.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/94190.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/94190.html</trackback:ping><description><![CDATA[
		<font color="#333333">
				<font face="宋体">   所有这些操作基于的环境和软件版本：WINDOWS2000 Pro、CVSNT2.0.4、WinCVS1.3.9.1Beta9 在前面的介绍CVSNT配合WinCVS进行用户管理的文章中，已经对CVSNT的用户管理，权限分配进行了比较详细的叙述，但是还有一些概念没有交待清楚，在这里，我会根据项目的实际需要，以及自己的一些经验给出一套用户管理、权限管理方案，在看这篇文章之前你最好已经阅读了前面的一篇文章，对用户的增加删除，权限的修改，等等这些操作能够了解，否则，会有一点困难。<br /><b><span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  1、 CVSNT的用户验证方式</span></b></font>
		</font>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">
						<br />
				</span>
		</b>
		<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  我们这里所讨论的是工作在pserver方式下。<br />  在CVSNT的文档中给出了两种验证方式，我总结了一下，可以这样称呼：Window和CVSNT混合验证方式，CVSNT独立验证方式。在前面的文章中，我们没有详细的给出这两种方式的内容，所讲述的启示就是混合验证方式。<br />  决定CVSNT工作于何种验证方式是由CVS的管理文件来决定的，这些管理文件处在库的目录下的CVSROOT目录中，这里可以得出结论，对于不同的库，可以给不同的验证方式。所以，在每个库建立的时候要首先设定好这些前提。<br />  下面的操作如果没有特殊指出则都是在客户端来进行管理的，下面首先是对一些控制原理和相关的文件做一些说明，如果你正在进行相关的模拟操作，请停下来暂时停止你的操作，因为这些操作的步骤是有先后的，如果你顺序不对，那么你可能就权限失效，进行不了下面的操作了。<br /></span>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  1．1 config文件</span>
		</b>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">
						<br />
				</span>
		</b>
		<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  在库建立好了以后，你还没有对控制文件进行修改之前，CVSNT是工作在混合验证方式之下的，这个时候，CVS服务器的管理员就是CVSNT的管理员，你以一个管理员身份登陆，检出你要操作的库的CVSROOT模块，看一下文件列表，控制CVSNT的验证工作方式的是config文件，你可以在文件列表中找到它，双击看看其中的内容，这里对我们最重要的就是第一个设置内容，你会看到下面的内容：<br />  # Set this to `no' if pserver shouldn't check system users/passwords<br />  #SystemAuth=yes<br />  第二行就是我们要修改的内容，默认状态是被注释掉的，SystemAuth有两个值yes和no<br />yes：pserver将使用系统用户数据库和passwd文件（这个文件后面会详细讲述）来共同验证（若passwd文件不存在或者文件中没有相应的资料，则用系统用户来进行验证）默认为yes<br />  no：所有的用户必须在passwd中存在，根据passwd的内容来进行用户的验证。<br />  我这里所阐述的方案就是工作在no的下面的，修改完之后提交到服务器，提交完毕服务器就处在CVSNT的独立验证模式下了。在这个工作方式下，NT本地的用户和CVSNT用户没有任何本质的联系和影响（仅仅是要建立一个别名）。<br /></span>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  1．2 passwd文件</span>
		</b>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">
						<br />
				</span>
		</b>
		<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  在讲述上面的时候提到了这个文件，在服务器工作在CVSNT验证模式下的时候，这个文件就可以称之为CVSNT的用户数据库，这个里面存储着用户列表，用户的密码，以及别名的一些信息。默认状态下这个文件是不存在的，所以，如果我们要在CVSNT验证模式下工作，必须建立这个文件。注意：这个文件是不能够在客户端进行修改的。这个文件的内容是相当简单的，就像下面：<br />  bach:ULtgRLXo7NRxs<br />  spwang:1sOp854gDF3DY<br />  melissa:tGX1fS8sun6rY:pubcvs<br />  qproj:XR4EZcEs0szik:pubcvs<br />  这里分别拿第一个用户bach和第三个用户melissa来进行说明，每一行代表一个用户，总共有三部分信息，用户名、密码、本地用户三部分之间使用冒号“:”来进行分割。<br />  用户名：就是登陆CVS的用户名<br />  密 码：用户的密码，这里是经过加密的，如果为空，那么就是空密码<br />  本地用户：CVS用户这个别名对应的本地用户，（跟本地用户没有任何其他关系，仅仅是别名的关系）<br />  如果在本地系统中存在一个用户名bash，那么要在CVS建立一个bach这样的用户就不需要在后面指出对应的系统用户，melissa后面的pubcvs就是系统用户，在本地系统上面存在的用户。对于要用命令增加这两种用户的格式如下：<br />  cvs passwd –a bach<br />  cvs passwd –r pubcvs –a melissa<br />  在库建立的时候可以在服务器上建立一个简单的passwd初始化文件，加一行<br />  cvsadmin:<br />  这样，就给出了一个cvsadmin这个空密码用户（本地系统中有这样的用户，就可以不加到后面去），然后在客户端来进行修改和以后的用户增加工作。注意：在客户端进行其他之前请先首先修改这个密码，以防止别人进行破坏。<br />  在服务器端建立了这个文件以后，就不用再手动进行修改了，当你在客户端进行密码或者用户的增加删除的时候，系统会自动进行这个文件的更新。这个文件是管理着CVSNT系统中的所有的用户，所以，要特别重视，不了解这个文件格式的，不要去随便修改，更加不要尝试在客户端进行修改！<br /></span>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  1．3 admin文件</span>
		</b>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">
						<br />
				</span>
		</b>
		<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  这个文件是指定CVSNT的管理员列表的文件，CVSNT会根据这个文件中的内容来判断一个用户是否是管理员。这个文件的内容很简单，是一个用户列表。类似下面<br />  user1<br />  user2<br />  user3<br />  这些代表user1,user2,user3都是管理员，当然，这些用户必须要存在才能够正确登陆系统来执行管理。<br />  这个文件默认状态下是没有的，但是，可以在客户端进行添加，在你的客户端进行新建这个文件然后add上去再commit一下，这个文件就可以上传到服务器，但是这个时候还没有生效，请修改checkoutlist这个文件，加入admin这一行，checkoutlist也可以在客户端进行修改再提交，这个时候admin就可以被系统自动的build了。<br />  Checkoutlist是维护的一个文件列表，可以放入系统自动build的用户自定义的系统文件列表，注意：对passwd没有用！！<br /></span>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  1．4 group文件</span>
		</b>
		<b>
				<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">
						<br />
				</span>
		</b>
		<span lang="EN-US" style="COLOR: #333333; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">  这个文件是定义系统的组，我们可以将同样性质的用户归入一个组，然后用给用户赋权限的方式给组赋权限，这样，一个组的用户就会具有同样的权限。Group的内容如下：<br />  group1:user1 user2 user3<br />  group2:me you he<br />  group3:tom honey<br />  有上面可以看出来，这个文件的内容也是相当的简单，首先是组的名称然后是冒号，接着是用户名，多个用户名之间用空格来进行分割。<br />  Group文件可以在客户端进行新建和修改，不用修改checkoutlist这个文件，系统会自动build这个文件并且使之生效。<br />  作为组里面的特定成员还可以赋给特定的权限，权限分为两类c,w,r和n，否定权限是有高的优先级的。<br />  好，上面已经介绍了本方案所涉及到的几个重要的文件以及修改方式。这里再强调一下，passwd只能够再服务器端进行建立和修改，不能够在客户端进行操作！<br />现在根据上面介绍的内容，可以开始你的操作了，下面给出修改顺序，库刚刚建立起来的时候，使用一个服务器上的本地管理员用户进行登陆检出CVSROOT模块。<br />  1、 现在服务器端加上passwd文件，给一个初始的用户，比如cvsadmin:<br />  2、 在客户端增加admin，将cvsadmin加入admin文件，作为出是管理员，并提交加入到库中。<br />  3、 在修改checkoutlist文件，加入admin，使其能够自动build。<br />  4、 最后修改config文件的SystemAuth=no，在提交之前要确认一下你上面的修改是否正确，如果提交了这个文件，CVSNT验证模式就开始生效了！<br />  5、 好，现在请修改你的参数再重新进行的登陆吧。因为你的系统已经切换了工作模式，你当前的用户已经失效了。<br />  完成了上面的步骤，整个服务器就会有效的工作在CVSNT验证模式下了。而group文件在你需要的任何时候可以加入。<br />  在上面的文章关于CVSNT的用户的管理方案的，在这里做一点补充，在后面的操作中全部是针对在客户端使用WinCVS来进行的（出了增加passwd）文件，其实，在我的实践当中config，passwd，admin，checkoutlist，这些文件的起始修改（初始化）都可以在新建了库以后一起完成，然后再让相应的库的管理员来进行相关的操作。<br /></span>
<img src ="http://www.blogjava.net/weibogao/aggbug/94190.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-16 12:38 <a href="http://www.blogjava.net/weibogao/archive/2007/01/16/94190.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Oracle的物化视图</title><link>http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Mon, 15 Jan 2007 04:05:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93914.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93914.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93914.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Oracle				的物化视图提供了强大的功能，可以用在不同的环境中。在不同的环境中，物化视图的作用也不相同。																																		数据仓库中的物化视图主要用于预先计算并保存表连接或聚集等耗时较多的操作的结果，这样，在执行查询时，就可以避免进行这些耗时的操作，而从快速的得到结果。在数据仓库中，还经常使用查询重写（...&nbsp;&nbsp;<a href='http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html'>阅读全文</a><img src ="http://www.blogjava.net/weibogao/aggbug/93914.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-15 12:05 <a href="http://www.blogjava.net/weibogao/archive/2007/01/15/93914.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Effective SQL</title><link>http://www.blogjava.net/weibogao/archive/2007/01/12/93392.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Fri, 12 Jan 2007 04:56:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/12/93392.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93392.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/12/93392.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93392.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93392.html</trackback:ping><description><![CDATA[
		<p>一.名词解释： <br />0。SQL 结构化查询语言(Structured Query Language) <br /><br />1。非关系型数据库系统 <br />做为第一代数据库系统的总称，其包括2种类型：“层次”数据库与“网状”数据库 <br /><br />“层次”数据库管理系统 eg:IBM&amp;IMS (Information  Management System) <br />特点:数据按层次模型组织 <br /><br />"网状"数据库 <br />特点：数据按网状模型组织 <br /><br />2。关系型数据库系统 <br />关系性数据库管理系统 (RDBMS) <br />eg:SQL/DS , DB2, Oracle ,Informix ,Unity,dBASE等 <br />特点：数据按二维的表格组织。 <br /><br />3。数据库(DataBase) <br />按一定结构存储在计算机中相互关联的数据的集合。 <br /><br />4。数据库管理系统DBMS(Database Management System) <br />一个通用的软件系统。就是让你怎么管理你的数据库。其中包括存储，安全，完整性管理<br />等。 <br /><br />5。数据库应用系统DBAS （Database Application System） <br />数据库应用程序系统，建立在DBMS基础之上的。就是一个面向用户的软件系统。 <br /><br />6。ANSI标准 （American National Standards Institute）美国国家标准委员会 <br />因为1999年第2次更新SQL，所以SQL又称为SQL99或SQL3（第3版，前2个版本分别为1986年<br />的sql ,1992 年的sql2/sql92）。 <br /><br />7。SQL语句的3种类型 <br />数据操作语句(Data Manipulation Language ) DML 关于数据操作命令的  eg:select,in<br />sert,update,delete <br />数据定义语句(Data Definition Language ) DDL     关于数据对象访问的  eg:create，<br /> drop <br />数据控制语句(Data Control Language) DCL         关于权限的  eg:grant ，revoke <br /><br /><br />8。PL/SQL Procedural Language/sql <br />用于oracle的语言 <br /><br />9.T-SQL  transact-sql <br />用于 microsoft sql server 和sybase adaptive server <br /><br />10。E.F.Codd关于关系型数据库12条检验原则（MYSQL，不支持视图和原子事物处理，所以<br />排除） <br />内容：暂略 <br /><br />11。数据库设计之新奥尔良方法。 <br />需求分析==》概念设计==》逻辑设计==》物理设计. <br />4个步骤的具体中以需求分析最重要. <br />需求分析的内容:暂略 <br />概念设计的内容:暂略 <br />逻辑设计的内容:暂略 <br />物理设计的内容:暂略 <br /><br /><br />二.数据库优化方案 <br />1.索引 <br />一 概述 <br /><br />  可以利用索引快速访问数据库表中的特定信息。索引是对数据库表中一个或多个列的值<br />进行排序的结构。 <br />  索引提供指针以指向存储在表中指定列的数据值，然后根据指定的排序次序排列这些指<br />针。 <br />  数据库使用索引的方式与使用书的目录很相似：通过搜索索引找到特定的值， <br />  然后跟随指针到达包含该值的行 <br /><br />索引是一个单独的、物理的数据库结构，它是某个表中一列或若干列值的集合和相应的指<br />向表中物理标识这些值的数据页的逻辑指针清单。 <br /><br />一个表的存储是由两部分组成的，一部分用来存放表的数据页面，另一部分存放索引页面<br />。索引就存放在索引页面上 <br /><br />二 索引的两种类型： <br /><br />聚集索引=簇集索引 <br /><br />聚集索引基于数据行的键值在表内排序和存储这些数据行。由于数据行按基于聚集索引键<br />的排序次序存储， <br />因此聚集索引对查找行很有效。每个表只能有一个聚集索引，因为数据行本身只能按一个<br />顺序存储。 <br />数据行本身构成聚集索引的最低级别。 <br /><br />只有当表包含聚集索引时，表内的数据行才按排序次序存储。如果表没有聚集索引， <br />则其数据行按堆集方式存储。 <br /><br />聚集索引对于那些经常要搜索范围值的列特别有效。使用聚集索引找到包含第一个值的行<br />后， <br />便可以确保包含后续索引值的行在物理相邻。例如，如果应用程序执行的一个查询经常检<br />索某一日期范围 <br />内的记录，则使用聚集索引可以迅速找到包含开始日期的行，然后检索表中所有相邻的行<br />， <br />直到到达结束日期。这样有助于提高此类查询的性能。同样，如果对从表中检索的数据进<br />行排序时 <br />经常要用到某一列，则可以将该表在该列上聚集（物理排序），避免每次查询该列时都进<br />行排序， <br />从而节省成本 <br /><br />非聚集索引 <br /><br />非聚集索引具有完全独立于数据行的结构。非聚集索引的最低行包含非聚集索引的键值，<br /><br />并且每个键值项都有指针指向包含该键值的数据行。数据行不按基于非聚集键的次序存储<br />。 <br /><br />在非聚集索引内，从索引行指向数据行的指针称为行定位器。 <br />行定位器的结构取决于数据页的存储方式是堆集还是聚集。对于堆集，行定位器是指向行<br />的指针。 <br />对于有聚集索引的表，行定位器是聚集索引键。 <br />只有在表上创建了聚集索引时，表内的行才按特定的顺序存储。这些行就基于聚集索引键<br />按顺序存储。 <br />如果一个表只有非聚集索引，它的数据行将按无序的堆集方式存储 <br />非聚集索引可以建多个,两者都能改善查询性能 <br /><br />非聚集索引与聚集索引一样有 B 树结构，但是有两个重大差别： <br />数据行不按非聚集索引键的顺序排序和存储。 <br />非聚集索引的叶层不包含数据页。 <br />相反，叶节点包含索引行。每个索引行包含非聚集键值以及一个或多个行定位器， <br />这些行定位器指向有该键值的数据行（如果索引不唯一，则可能是多行）。 <br />非聚集索引可以在有聚集索引的表、堆集或索引视图上定义 <br /><br /><br />聚集索引--&gt;顺序表结构.其物理数据和逻辑排序紧邻. <br />非聚集索引--&gt;单链表结构.起物理和逻辑排序不按顺序排列. <br /><br />打个比方. <br />一本字典,你现在查一个陈字.你有2种方法.首先,你在知道他念chen的情况下去按照拼音字<br />母去查找.他是排在字母A,B <br />于是你很容易的就找到"陈"字.第2种方法则是按编旁查找,先找到耳朵旁,去找到一个临时<br />的编旁表在去找"东"这个字,然后按照给出的 <br />页数找到相应的位置. <br />显然,第一种方法就是聚集索引,按照物理位置根据排序来查找. <br />第2种方法则是非聚集索引,按照一个临时索引来查找. <br /><br />另外 <br />唯一索引 <br /><br />唯一索引可以确保索引列不包含重复的值。在多列唯一索引的情况下，该索引可以确保索<br />引列中每个值组 <br />合都是唯一的。唯一索引既是索引也是约束。 <br /><br />复合索引 <br />索引项是多个的就叫组合索引，也叫复合索引。复合索引使用时需要注意索引项的次序。<br /><br /><br />二 索引的创建 <br /><br />有两种方法可以在 SQL Server 内定义索引: CREATE INDEX 语句和CREATE TABLE 语句 <br /><br /><br />CREATE TABLE支持在创建索引时使用下列约束： <br /><br />PRIMARY KEY 创建唯一索引来强制执行主键 <br />UNIQUE 创建唯一索引 <br />CLUSTERED 创建聚集索引 <br />NONCLUSTERED 创建非聚集索引 <br /><br />注: 1 定义索引时，可以指定每列的数据是按升序还是降序存储。如果不指定，则默认为<br />升序 <br />   2 支持在计算列上创建索引 <br />   3 为索引指定填充因子 <br />     可标识填充因子来指定每个索引页的填满程度。索引页上的空余空间量很重要， <br />     因为当索引页填满时，系统必须花时间拆分它以便为新行腾出空间。 <br /><br /><br />三 索引的维护语句 <br /><br />DBCC DBREINDEX    重建指定数据库中表的一个或多个索引 <br />DBCC INDEXFRAG　　整理指定的表或视图的聚集索引和辅助索引碎片 <br /><br />比较 <br /><br />            速度    兼容性     日志影响      数据访问影响       额外磁盘空间 <br /><br />DBCC        最快      最好     大,但能通过把   操作过程中数据不   需要大 <br />DBREINDEX             可以重   故障还原模型设  能访问，影响大 <br />                     建所有   为简单减少日志    <br />                     有索引 <br /><br />DBCC        慢       但可   必须分   小              数据未被锁定        需要小<br /><br />INDEXDEFRAG          随时终 别指定 <br />                    止执行   <br />                               <br /><br />drop index    中等  必须分   大,但能通过把    仅在操作执行时    中等，操作在  <br />  <br />create index        别指定   故障还原模型设   锁定数据          tempdb中进行 <br /><br />                            为简单减少日志 <br /><br /><br />四 查看索引的方法 <br /><br />sp_indexes        返回指定远程表的索引信息 <br />INDEXKEY_PROPERTY 返回有关索引键的信息 <br />sysindexes系统表  数据库中的每个索引和表在表中各占一行，该表存储在每个数据库中<br /><br /><br /><br />五 可以通过执行计划 <br />  查看sql语句执行时是否建立在索引之上 <br /><br />比如 <br />CREATE TABLE Test <br />(Field_1 int NOT NULL, <br />Field_2 int CONSTRAINT PK_Test <br />PRIMARY KEY CLUSTERED (Field_1)) <br /><br />CREATE index IX_Test ON Test (Field_2) <br /><br />1 SELECT * FROM Test WHERE Field_2 =408 <br /> 执行计划可以看出使用了IX_Test索引 <br />2 SELECT * FROM Test WHERE Field_1 =1 <br /> 执行计划可以看出使用了PK_Test <br />3 但如果是SELECT * FROM Test with (index(IX_Test)) WHERE Field_1 =1 <br /> 则指定使用索引 <br /><br /><br />六 索引的具体使用 <br /><br />1） 索引的设计 <br />A:尽量避免表扫描 <br />检查你的查询语句的where子句，因为这是优化器重要关注的地方。包含在where里面的每<br />一列（column)都是可能的侯选索引，为能达到最优的性能，考虑在下面给出的例子：对于<br />在where子句中给出了column1这个列。 <br />下面的两个条件可以提高索引的优化查询性能！ <br />第一：在表中的column1列上有一个单索引 <br />第二：在表中有多索引，但是column1是第一个索引的列 <br />避免定义多索引而column1是第二个或后面的索引，这样的索引不能优化服务器性能 <br />例如：下面的例子用了pubs数据库。 <br />SELECT au_id, au_lname, au_fname FROM authors <br />WHERE au_lname = ’White’ <br />按下面几个列上建立的索引将会是对优化器有用的索引 <br />?au_lname <br />?au_lname, au_fname <br />而在下面几个列上建立的索引将不会对优化器起到好的作用 <br />?au_address <br />?au_fname, au_lname <br />考虑使用窄的索引在一个或两个列上，窄索引比多索引和复合索引更能有效。用窄的索引<br />，在每一页上 <br />将会有更多的行和更少的索引级别（相对与多索引和复合索引而言），这将推进系统性能<br />。 <br />对于多列索引，SQL Server维持一个在所有列的索引上的密度统计（用于联合）和在第一<br />个索引上的 <br />histogram（柱状图）统计。根据统计结果，如果在复合索引上的第一个索引很少被选择使<br />用，那么优化器对很多查询请求将不会使用索引。 <br />有用的索引会提高select语句的性能，包括insert,uodate,delete。 <br />但是，由于改变一个表的内容，将会影响索引。每一个insert,update,delete语句将会使<br />性能下降一些。实验表明，不要在一个单表上用大量的索引，不要在共享的列上（指在多<br />表中用了参考约束）使用重叠的索引。 <br />在某一列上检查唯一的数据的个数，比较它与表中数据的行数做一个比较。这就是数据的<br />选择性，这比较结果将会帮助你决定是否将某一列作为侯选的索引列，如果需要，建哪一<br />种索引。你可以用下面的查询语句返回某一列的不同值的数目。 <br />select count(distinct cloumn_name) from table_name <br />假设column_name是一个10000行的表，则看column_name返回值来决定是否应该使用，及应<br />该使用什么索引。 <br />Unique values Index <br /><br />5000 Nonclustered index <br />20 Clustered index <br />3 No index <br /><br /><br />2) 镞索引和非镞索引的选择 <br /><br />&lt;1:&gt;镞索引是行的物理顺序和索引的顺序是一致的。页级，低层等索引的各个级别上都包<br />含实际的数据页。一个表只能是有一个镞索引。由于update,delete语句要求相对多一些的<br />读操作，因此镞索引常常能加速这样的操作。在至少有一个索引的表中，你应该有一个镞<br />索引。 <br />在下面的几个情况下，你可以考虑用镞索引： <br />例如： 某列包括的不同值的个数是有限的（但是不是极少的） <br />顾客表的州名列有50个左右的不同州名的缩写值，可以使用镞索引。 <br />例如： 对返回一定范围内值的列可以使用镞索引，比如用between,&gt;,&gt;=,&lt;,&lt;=等等来对列<br />进行操作的列上。 <br />select * from sales where ord_date between ’5/1/93’ and ’6/1/93’ <br />例如： 对查询时返回大量结果的列可以使用镞索引。 <br />SELECT * FROM phonebook WHERE last_name = ’Smith’ <br /><br />当有大量的行正在被插入表中时，要避免在本表一个自然增长（例如，identity列）的列<br />上建立镞索引。如果你建立了镞的索引，那么insert的性能就会大大降低。因为每一个插<br />入的行必须到表的最后，表的最后一个数据页。 <br />当一个数据正在被插入（这时这个数据页是被锁定的），所有的其他插入行必须等待直到<br />当前的插入已经结束。 <br />一个索引的叶级页中包括实际的数据页，并且在硬盘上的数据页的次序是跟镞索引的逻辑<br />次序一样的。 <br /><br />&lt;2:&gt;一个非镞的索引就是行的物理次序与索引的次序是不同的。一个非镞索引的叶级包含<br />了指向行数据页的指针。 <br />在一个表中可以有多个非镞索引，你可以在以下几个情况下考虑使用非镞索引。 <br />在有很多不同值的列上可以考虑使用非镞索引 <br />例如：一个part_id列在一个part表中 <br />select * from employee where emp_id = ’pcm9809f’ <br />查询语句中用order by 子句的列上可以考虑使用镞索引 <br /><br /><br /><br />3) 一个表列如果设为主键(primary key),它会自动生成一个聚簇索引 <br />这时不能直接使用Drop index Table1.Tableindex1语句 <br />必须删除主键约束，用语句:alter table table1 drop constraint 约束名(如pk_xxx) <br /><br /><br /><br />七.全文索引 <br />use pubs <br />　　go <br /><br />　　--打开数据库全文索引的支持 <br /><br />　execute sp_fulltext_database 'enable' <br />　go <br /><br />　　--建立全文目录ft_titles <br /><br />　　execute sp_fulltext_catalog 'ft_titles', 'create' <br />　　go <br /><br />　　--为titles表建立全文索引数据元，UPKCL_titleidind是主键所建立的唯一索引，可<br />由sp_help titles得知 <br /><br />　　execute sp_fulltext_table 'titles','create', 'ft_titles', 'UPKCL_titleidin<br />d' <br />　　go <br /><br />　　--设置全文索引列名 <br /><br />　　exec sp_fulltext_column 'titles', 'title', 'add' <br />　　go <br />　　exec sp_fulltext_column 'titles', 'notes', 'add' <br />　　go <br /><br />　　--建立全文索引 <br /><br />　　exec sp_fulltext_table 'titles', 'activate' <br />　　go <br /><br />　　--填充全文索引目录 <br /><br />　　exec sp_fulltext_catalog 'ft_titles', 'start_full' <br />　　go <br /><br />　　--使用contains和freetext <br /><br />　　select title, notes from titles <br />　　where contains(title, '"computer Cooking"') <br />　　go <br />　　select title, notes from titles <br />　　where freetext(title, 'computer Cooking') <br />　　go <br />　　select title, notes from titles <br />　　where freetext(title, '"computer Cooking"') <br />　　go <br />　　select title, notes from titles <br />　　where contains(title, 'computer') <br />　　go <br />　　select title, notes from titles <br />　　where freetext (*, 'computer') <br />　　go <br /><br />这里提一下google的搜索引擎的原理. <br />他把每个字词都做为单元去查询. <br />打个比方:我在字典里查询,现在我要搜索"树型"这个词,他会把这个树型这个词全文扫描一<br />遍,生成一个二叉树.并记下他的页数. <br />然后当我第2次查找的时候显然这个"记忆"提示,然后"提取".如果你对某一个字段做了全文<br />索引的话，他会全文扫描表一遍,然后纪录下 <br />相应的纪录,生成二叉树. <br />如果我要查找"树叶",同理也可以得出页数.但当我们去查找一下"树型结构"他则会把"树型<br />"和"树型结构"都"纪录"下来. <br /><br />八.巧妙的使用索引. <br />SELECT SUM(quantity) AS quantity FROM test WHERE... <br />1.若WHERE 里用的是字段与常量比较，MSSQL会自动引用该字段上的索引；若用的是变量，<br />MSSQL不会自动引用该字段上的索引而是根据聚集索引进行扫描 <br />2.加上with(index(索引名))指定索引，即： <br />SELECT SUM(quantity) AS quantity FROM with(index(索引名)) test WHERE... <br />指定索引后，WHERE 里不论是常量还是变量，MSSQL都根据指定的索引进行扫描 <br />3.DBCC DBREINDEX执行并不一定能优化MSSQL性能，慎用 <br />4.如果在pub_id上建立索引的话 <br />select * from titles where pub_id-500 &gt;1000   ---------(a) <br />select * from titles where pub_id &gt;1000+500  -----------(b) <br />请选用(b)语句,这样的话，他会利用索引,而(a)的话由于对字段操作了,所以不会利用索引<br />. <br />5.尽量避免用like语句, <br />如果去查找baa%,caa%的话 <br />如果是like '%aa%','_aa%','[m-z]o%'  则根本不会用到索引. <br />替换方法.columns like 'baa%' or  columns like 'caa %' <br />6什么情况下应不建或少建索引 <br />a.表记录太少 .因为索引的话，要对数据库往返2次操作,如果1个表只有几行字段的话，数<br />据库会对他的纪录一次性全部取出来,这样的效率要远远高于索引. <br />b.经常insert,delete,update的表  对一些经常处理的业务表应在查询允许的情况下尽量<br />减少索引 <br />c.数据重复且分布平均的表字段,如:性别字段,各占50%的话，你即使建了,也起不到明显的<br />作用. <br />d.经常和主字段一块查询但主字段索引值比较多的表字段 <br />表经常按收费序号、户标识编号、抄表日期、电费发生年月、操作标志来具体查询某一笔<br />收款的情况，如果将所有的字段都建在一个索引里那将会增加数据的修改、插入、删除时<br />间，从实际上分析一笔收款如果按收费序号索引就已经将记录减少到只有几条，如果再按<br />后面的几个字段索引查询将对性能不产生太大的影响。 <br />e.如果一个表的记录达到100万以上的话，要对其中一个字段建索引可能要花很长的时间，<br />甚至导致服务器数据库死机，因为在建索引的时候 ORACLE要将索引字段所有的内容取出并<br />进行全面排序，数据量大的话可能导致服务器排序内存不足而引用磁盘交换空间进行，这<br />将严重影响服务器数据库的工作。解决方法是增大数据库启动初始化中的排序内存参数，<br />如果要进行大量的索引修改可以设置10M以上的排序内存（ORACLE缺省大小为64K），在索<br />引建立完成后应将参数修改回来，因为在实际OLTP数据库应用中一般不会用到这么大的排<br />序内存。 <br /><br /><br />以下转载 <br />great_domino 的 Blog <br /><br />探讨如何在有着1000万条数据的MS SQL SERVER数据库中实现快速的数据提取和数据分页。<br />以下代码说明了我们实例中数据库的“红头文件”一表的部分数据结构： <br /><br />CREATE TABLE [dbo].[TGongwen] (    --TGongwen是红头文件表名 <br /><br />  [Gid] [int] IDENTITY (1, 1) NOT NULL , <br />--本表的id号，也是主键 <br /><br />  [title] [varchar] (80) COLLATE Chinese_PRC_CI_AS NULL , <br />--红头文件的标题 <br /><br />  [fariqi] [datetime] NULL , <br />--发布日期 <br /><br />  [neibuYonghu] [varchar] (70) COLLATE Chinese_PRC_CI_AS NULL , <br />--发布用户 <br /><br />  [reader] [varchar] (900) COLLATE Chinese_PRC_CI_AS NULL , <br /><br />--需要浏览的用户。每个用户中间用分隔符“,”分开 <br /><br />) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] <br /><br />GO <br /><br /><br />　　下面，我们来往数据库中添加1000万条数据： <br /><br />declare @i int <br /><br />set @i=1 <br /><br />while @i&lt;=250000 <br /><br />begin <br /><br />   insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-2-5','通<br />信科','通信科,办公室,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经<br />侦支队, 户政科,治安支队,外事科','这是最先的25万条记录') <br /><br />   set @i=@i+1 <br /><br />end <br /><br />GO <br /><br /><br /><br />declare @i int <br /><br />set @i=1 <br /><br />while @i&lt;=250000 <br /><br />begin <br /><br />   insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-9-16','办<br />公室','办公室,通信科,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经<br />侦支队,户政科,外事科','这是中间的25万条记录') <br /><br />   set @i=@i+1 <br /><br />end <br /><br />GO <br /><br /><br /><br />declare @h int <br /><br />set @h=1 <br /><br />while @h&lt;=100 <br /><br />begin <br /><br />declare @i int <br /><br />set @i=2002 <br /><br />while @i&lt;=2003 <br /><br />begin <br /><br />declare @j int <br /><br />       set @j=0 <br /><br />       while @j&lt;50 <br /><br />           begin <br /><br />declare @k int <br /><br />           set @k=0 <br /><br />           while @k&lt;50 <br /><br />           begin <br /><br />   insert into Tgongwen(fariqi,neibuyonghu,reader,title) values(cast(@i as var<br />char(4))+'-8-15 3:'+cast(@j as varchar(2))+':'+cast(@j as varchar(2)),'通信科'<br />,'办公室,通信科,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支<br />队,户政科,外事科','这是最后的50万条记录') <br /><br />           set @k=@k+1 <br /><br />           end <br /><br />set @j=@j+1 <br /><br />       end <br /><br />set @i=@i+1 <br /><br />end <br /><br />set @h=@h+1 <br /><br />end <br /><br />GO <br /><br /><br /><br />declare @i int <br /><br />set @i=1 <br /><br />while @i&lt;=9000000 <br /><br />begin <br /><br />   insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-5-5','通<br />信科','通信科,办公室,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经<br />侦支队, 户政科,治安支队,外事科','这是最后添加的900万条记录') <br /><br />   set @i=@i+1000000 <br /><br />end <br /><br />GO <br /><br />通过以上语句，我们创建了25万条由于2004年2月5日发布的记录，25万条由办公室于2004<br />年9月6日发布的记录，2002年和2003年各 100个2500条相同日期、不同分秒的记录（共50<br />万条），还有由通信科于2004年5月5日发布的900万条记录，合计1000万条。 <br /><br />何时使用聚集索引或非聚集索引 <br /><br />　　下面的表总结了何时使用聚集索引或非聚集索引（很重要）。 <br /><br />　　动作描述 <br />　　　使用聚集索引 <br />　　　使用非聚集索引 <br /><br />　　列经常被分组排序 <br />　　　应 <br />　　　应 <br /><br />　　返回某范围内的数据 <br />　　　应 <br />　　　不应 <br /><br />　　一个或极少不同值 <br />　　　不应 <br />　　　不应 <br /><br />　　小数目的不同值 <br />　　　应 <br />　　　不应 <br /><br />　　大数目的不同值 <br />　　　不应 <br />　　　应 <br /><br />　　频繁更新的列 <br />　　　不应 <br />　　　应 <br /><br />　　外键列 <br />　　　应 <br />　　　应 <br /><br />　　主键列 <br />　　　应 <br />　　　应 <br /><br />　　频繁修改索引列 <br />　　　不应 <br />　　　应 <br /><br /><br />　　事实上，我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如：返<br />回某范围内的数据一项。比如您的某个表有一个时间列，恰好您把聚合索引建立在了该列<br />，这时您查询2004年1月1日至2004年10月1日之间的全部数据时，这个速度就将是很快的，<br />因为您的这本字典正文是按日期进行排序的，聚类索引只需要找到要检索的所有数据中的<br />开头和结尾数据即可；而不像非聚集索引，必须先查到目录中查到每一项数据对应的页码<br />，然后再根据页码查到具体内容。 <br /><br />（三）结合实际，谈索引使用的误区 <br /><br />　　理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引，但在实<br />践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践<br />中遇到的实际问题来谈一下索引使用的误区，以便于大家掌握索引建立的方法。 <br /><br />　　1、主键就是聚集索引 <br /><br />　　这种想法笔者认为是极端错误的，是对聚集索引的一种浪费。虽然SQL SERVER默认是<br />在主键上建立聚集索引的。 <br /><br />　　通常，我们会在每个表中都建立一个ID列，以区分每条数据，并且这个ID列是自动增<br />大的，步长一般为1。我们的这个办公自动化的实例中的列 Gid就是如此。此时，如果我们<br />将这个列设为主键，SQL SERVER会将此列默认为聚集索引。这样做有好处，就是可以让您<br />的数据在数据库中按照ID进行物理排序，但笔者认为这样做意义不大。 <br /><br />　　显而易见，聚集索引的优势是很明显的，而每个表中只能有一个聚集索引的规则，这<br />使得聚集索引变得更加珍贵。 <br /><br />　　从我们前面谈到的聚集索引的定义我们可以看出，使用聚集索引的最大好处就是能够<br />根据查询要求，迅速缩小查询范围，避免全表扫描。在实际应用中，因为ID号是自动生成<br />的，我们并不知道每条记录的ID号，所以我们很难在实践中用ID号来进行查询。这就使让<br />ID号这个主键作为聚集索引成为一种资源浪费。其次，让每个ID号都不同的字段作为聚集<br />索引也不符合“大数目的不同值情况下不应建立聚合索引”规则；当然，这种情况只是针<br />对用户经常修改记录内容，特别是索引项的时候会负作用，但对于查询速度并没有影响。<br /><br /><br />　　在办公自动化系统中，无论是系统首页显示的需要用户签收的文件、会议还是用户进<br />行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户<br />名”。 <br /><br />　　通常，办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语<br />句可以仅仅限制当前用户尚未签收的情况，但如果您的系统已建立了很长时间，并且数据<br />量很大，那么，每次每个用户打开首页的时候都进行一次全表扫描，这样做意义是不大的<br />，绝大多数的用户1个月前的文件都已经浏览过了，这样做只能徒增数据库的开销而已。事<br />实上，我们完全可以让用户打开系统首页时，数据库仅仅查询这个用户近3个月来未阅览的<br />文件，通过“日期”这个字段来限制表扫描，提高查询速度。如果您的办公自动化系统已<br />经建立的2年，那么您的首页显示速度理论上将是原来速度8倍，甚至更快。 <br /><br />　　在这里之所以提到“理论上”三字，是因为如果您的聚集索引还是盲目地建在ID这个<br />主键上时，您的查询速度是没有这么高的，即使您在“日期”这个字段上建立的索引（非<br />聚合索引）。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现（3个月<br />内的数据为25万条）： <br /><br />　　（1）仅在主键上建立聚集索引，并且不划分时间段： <br /><br />Select gid,fariqi,neibuyonghu,title from tgongwen <br /><br />　　用时：128470毫秒（即：128秒） <br /><br />　　（2）在主键上建立聚集索引，在fariq上建立非聚集索引： <br /><br />select gid,fariqi,neibuyonghu,title from Tgongwen <br /><br />where fariqi&gt; dateadd(day,-90,getdate()) <br /><br />　　用时：53763毫秒（54秒） <br /><br />　　（3）将聚合索引建立在日期列（fariqi）上： <br /><br />select gid,fariqi,neibuyonghu,title from Tgongwen <br /><br />where fariqi&gt; dateadd(day,-90,getdate()) <br /><br />　　用时：2423毫秒（2秒） <br /><br />　　虽然每条语句提取出来的都是25万条数据，各种情况的差异却是巨大的，特别是将聚<br />集索引建立在日期列时的差异。事实上，如果您的数据库真的有 1000万容量的话，把主键<br />建立在ID列上，就像以上的第1、2种情况，在网页上的表现就是超时，根本就无法显示。<br />这也是我摒弃ID列作为聚集索引的一个最重要的因素。 <br /><br />　　得出以上速度的方法是：在各个select语句前加：declare @d datetime <br /><br />set @d=getdate() <br /><br />并在select语句后加： <br /><br />select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate()) <br /><br />　　2、只要建立索引就能显著提高查询速度 <br /><br />　　事实上，我们可以发现上面的例子中，第2、3条语句完全相同，且建立索引的字段也<br />相同；不同的仅是前者在fariqi字段上建立的是非聚合索引，后者在此字段上建立的是聚<br />合索引，但查询速度却有着天壤之别。所以，并非是在任何字段上简单地建立索引就能提<br />高查询速度。 <br /><br />　　从建表的语句中，我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同<br />记录。在此字段上建立聚合索引是再合适不过了。在现实中，我们每天都会发几个文件，<br />这几个文件的发文日期就相同，这完全符合建立聚集索引要求的：“既不能绝大多数都相<br />同，又不能只有极少数相同”的规则。由此看来，我们建立“适当”的聚合索引对于我们<br />提高查询速度是非常重要的。 <br /><br />　　3、把所有需要提高查询速度的字段都加进聚集索引，以提高查询速度 <br /><br />　　上面已经谈到：在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户<br />名”。既然这两个字段都是如此的重要，我们可以把他们合并起来，建立一个复合索引（<br />compound index）。 <br /><br />　　很多人认为只要把任何字段加进聚集索引，就能提高查询速度，也有人感到迷惑：如<br />果把复合的聚集索引字段分开查询，那么查询速度会减慢吗？带着这个问题，我们来看一<br />下以下的查询速度（结果集都是25万条数据）：（日期列fariqi首先排在复合聚集索引的<br />起始列，用户名neibuyonghu排在后列） <br /><br />　　（1）select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi&gt;'2004-<br />5-5' <br /><br />　　查询速度：2513毫秒 <br /><br />　　（2）select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi&gt;'2004-<br />5-5' and neibuyonghu='办公室' <br /><br />　　查询速度：2516毫秒 <br /><br />　　（3）select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='<br />办公室' <br /><br />　　查询速度：60280毫秒 <br /><br />　　从以上试验中，我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复<br />合聚集索引的全部列的查询速度是几乎一样的，甚至比用上全部的复合索引列还要略快（<br />在查询结果集数目一样的情况下）；而如果仅用复合聚集索引的非起始列作为查询条件的<br />话，这个索引是不起任何作用的。当然，语句1、2的查询速度一样是因为查询的条目数一<br />样，如果复合索引的所有列都用上，而且查询结果少的话，这样就会形成“索引覆盖”，<br />因而性能可以达到最优。同时，请记住：无论您是否经常使用聚合索引的其他列，但其前<br />导列一定要是使用最频繁的列。 <br /><br />（四）其他书上没有的索引使用经验总结 <br /><br />　　1、用聚合索引比用不是聚合索引的主键速度快 <br /><br />　　下面是实例语句：（都是提取25万条数据） <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' <br /><br />　　使用时间：3326毫秒 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid&lt;=250000 <br /><br />　　使用时间：4470毫秒 <br /><br />　　这里，用聚合索引比用不是聚合索引的主键速度快了近1/4。 <br /><br />　　2、用聚合索引比用一般的主键作order by时速度快，特别是在小数据量情况下 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi <br /><br />　　用时：12936 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid <br /><br />　　用时：18843 <br /><br />　　这里，用聚合索引比用一般的主键作order by时，速度快了3/10。事实上，如果数据<br />量很小的话，用聚集索引作为排序列要比使用非聚集索引速度快得明显的多；而数据量如<br />果很大的话，如10万以上，则二者的速度差别不明显。 <br /><br />　　3、使用聚合索引内的时间段，搜索时间会按数据占整个数据表的百分比成比例减少，<br />而无论聚合索引使用了多少个 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;'2004-1-<br />1' <br /><br />　　用时：6343毫秒（提取100万条） <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;'2004-6-<br />6' <br /><br />　　用时：3170毫秒（提取50万条） <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' <br /><br />　　用时：3326毫秒（和上句的结果一模一样。如果采集的数量一样，那么用大于号和等<br />于号是一样的） <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;'2004-1-<br />1' and fariqi&lt;'2004-6-6' <br /><br />　　用时：3280毫秒 <br /><br />　　4 、日期列不会因为有分秒的输入而减慢查询速度 <br /><br />　　下面的例子中，共有100万条数据，2004年1月1日以后的数据有50万条，但只有两个不<br />同的日期，日期精确到日；之前有数据50万条，有5000个不同的日期，日期精确到秒。 <br /><br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;'2004-1-<br />1' order by fariqi <br /><br />　　用时：6390毫秒 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&lt;'2004-1-<br />1' order by fariqi <br /><br />　　用时：6453毫秒 <br /><br />　　（五）其他注意事项 <br /><br />　　“水可载舟，亦可覆舟”，索引也一样。索引有助于提高检索性能，但过多或不当的<br />索引也会导致系统低效。因为用户在表中每加进一个索引，数据库就要做更多的工作。过<br />多的索引甚至会导致索引碎片。 <br /><br />　　所以说，我们要建立一个“适当”的索引体系，特别是对聚合索引的创建，更应精益<br />求精，以使您的数据库能得到高性能的发挥。 <br /><br />　　当然，在实践中，作为一个尽职的数据库管理员，您还要多测试一些方案，找出哪种<br />方案效率最高、最为有效。 <br /><br />二、改善SQL语句 <br /><br />　　很多人不知道SQL语句在SQL SERVER中是如何执行的，他们担心自己所写的SQL语句会<br />被SQL SERVER误解。比如： <br /><br />select * from table1 where name='zhangsan' and tID &gt; 10000 <br /><br />　　和执行: <br /><br />select * from table1 where tID &gt; 10000 and name='zhangsan' <br /><br />　　一些人不知道以上两条语句的执行效率是否一样，因为如果简单的从语句先后上看，<br />这两个语句的确是不一样，如果tID是一个聚合索引，那么后一句仅仅从表的10000条以后<br />的记录中查找就行了；而前一句则要先从全表中查找看有几个name='zhangsan'的，而后再<br />根据限制条件条件 tID&gt;10000来提出查询结果。 <br /><br />　　事实上，这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”，它可以<br />计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间，也就是说，它能<br />实现自动优化。 <br /><br />　　虽然查询优化器可以根据where子句自动的进行查询优化，但大家仍然有必要了解一下<br />“查询优化器”的工作原理，如非这样，有时查询优化器就会不按照您的本意进行快速查<br />询。 <br /><br />　　在查询分析阶段，查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否<br />有用。如果一个阶段可以被用作一个扫描参数（SARG），那么就称之为可优化的，并且可<br />以利用索引快速获得所需数据。 <br /><br />　　SARG的定义：用于限制搜索的一个操作，因为它通常是指一个特定的匹配，一个值得<br />范围内的匹配或者两个以上条件的AND连接。形式如下： <br /><br />列名 操作符 &lt;常数 或 变量&gt; <br /><br />或 <br /><br />&lt;常数 或 变量&gt; 操作符列名 <br /><br />　　列名可以出现在操作符的一边，而常数或变量出现在操作符的另一边。如： <br /><br />Name=’张三’ <br /><br />价格&gt;5000 <br /><br />5000&lt;价格 <br /><br />Name=’张三’ and 价格&gt;5000 <br /><br />　　如果一个表达式不能满足SARG的形式，那它就无法限制搜索的范围了，也就是SQL SE<br />RVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足S<br />ARG形式的表达式来说是无用的。 <br /><br />　　介绍完SARG后，我们来总结一下使用SARG以及在实践中遇到的和某些资料上结论不同<br />的经验： <br /><br />　　1、Like语句是否属于SARG取决于所使用的通配符的类型 <br /><br />　　如：name like ‘张%’ ，这就属于SARG <br /><br />　　而：name like ‘%张’ ,就不属于SARG。 <br /><br />　　原因是通配符%在字符串的开通使得索引无法使用。 <br /><br />　　2、or 会引起全表扫描 <br /><br />Name=’张三’ and 价格&gt;5000 符号SARG，而：Name=’张三’ or 价格&gt;5000 则不符合S<br />ARG。使用or会引起全表扫描。 <br /><br />　　3、非操作符、函数引起的不满足SARG形式的语句 <br /><br />　　不满足SARG形式的语句最典型的情况就是包括非操作符的语句，如：NOT、!=、&lt;&gt;、!<br />&lt;、!&gt;、NOT EXISTS、NOT IN、NOT LIKE等，另外还有函数。下面就是几个不满足SARG形式<br />的例子： <br /><br />ABS(价格)&lt;5000 <br /><br />Name like ‘%三’ <br /><br />　　有些表达式，如： <br /><br />WHERE 价格*2&gt;5000 <br /><br />　　SQL SERVER也会认为是SARG，SQL SERVER会将此式转化为： <br /><br />WHERE 价格&gt;2500/2 <br /><br />　　但我们不推荐这样使用，因为有时SQL SERVER不能保证这种转化与原始表达式是完全<br />等价的。 <br /><br />　　4、IN 的作用相当与OR <br /><br />　　语句： <br /><br />Select * from table1 where tid in (2,3) <br /><br />　　和 <br /><br />Select * from table1 where tid=2 or tid=3 <br /><br />　　是一样的，都会引起全表扫描，如果tid上有索引，其索引也会失效。 <br /><br />　　5、尽量少用NOT <br /><br />　　6、exists 和 in 的执行效率是一样的 <br /><br />　　很多资料上都显示说，exists要比in的执行效率要高，同时应尽可能的用not exists<br />来代替not in。但事实上，我试验了一下，发现二者无论是前面带不带not，二者之间的执<br />行效率都是一样的。因为涉及子查询，我们试验这次用SQL SERVER自带的pubs数据库。运<br />行前我们可以把SQL SERVER的statistics I/O状态打开。 <br /><br />　　（1）select title,price from titles where title_id in (select title_id fro<br />m sales where qty&gt;30) <br /><br />　　该句的执行结果为： <br /><br />　　表 'sales'。扫描计数 18，逻辑读 56 次，物理读 0 次，预读 0 次。 <br /><br />　　表 'titles'。扫描计数 1，逻辑读 2 次，物理读 0 次，预读 0 次。 <br /><br />　　（2）select title,price from titles where exists (select * from sales wher<br />e sales.title_id=titles.title_id and qty&gt;30) <br /><br />　　第二句的执行结果为： <br /><br />　　表 'sales'。扫描计数 18，逻辑读 56 次，物理读 0 次，预读 0 次。 <br /><br />　　表 'titles'。扫描计数 1，逻辑读 2 次，物理读 0 次，预读 0 次。 <br /><br />　　我们从此可以看到用exists和用in的执行效率是一样的。 <br /><br />　　7、用函数charindex()和前面加通配符%的LIKE执行效率一样 <br /><br />　　前面，我们谈到，如果在LIKE前面加上通配符%，那么将会引起全表扫描，所以其执行<br />效率是低下的。但有的资料介绍说，用函数charindex()来代替LIKE速度会有大的提升，经<br />我试验，发现这种说明也是错误的： <br /><br />select gid,title,fariqi,reader from tgongwen where charindex('刑侦支队',reader<br />)&gt;0 and fariqi&gt;'2004-5-5' <br /><br />　　用时：7秒，另外：扫描计数 4，逻辑读 7155 次，物理读 0 次，预读 0 次。 <br /><br />select gid,title,fariqi,reader from tgongwen where reader like '%' + '刑侦支队<br />' + '%' and fariqi&gt;'2004-5-5' <br /><br />　　用时：7秒，另外：扫描计数 4，逻辑读 7155 次，物理读 0 次，预读 0 次。 <br /><br />　　8、union并不绝对比or的执行效率高 <br /><br />　　我们前面已经谈到了在where子句中使用or会引起全表扫描，一般的，我所见过的资料<br />都是推荐这里用union来代替or。事实证明，这种说法对于大部分都是适用的。 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' or gid&gt;9990000 <br /><br />　　用时：68秒。扫描计数 1，逻辑读 404008 次，物理读 283 次，预读 392163 次。 <br /><br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' <br /><br />union <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid&gt;9990000 <br /><br />　　用时：9秒。扫描计数 8，逻辑读 67489 次，物理读 216 次，预读 7499 次。 <br /><br />　　看来，用union在通常情况下比用or的效率要高的多。 <br /><br />　　但经过试验，笔者发现如果or两边的查询列是一样的话，那么用union则反倒和用or的<br />执行速度差很多，虽然这里union扫描的是索引，而or扫描的是全表。 <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' or fariqi='2004-2-5' <br /><br />　　用时：6423毫秒。扫描计数 2，逻辑读 14726 次，物理读 1 次，预读 7176 次。 <br /><br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-<br />16' <br /><br />union <br /><br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where  fariqi='2004-2<br />-5' <br /><br />　　用时：11640毫秒。扫描计数 8，逻辑读 14806 次，物理读 108 次，预读 1144 次。<br /><br /><br />　　9、字段提取要按照“需多少、提多少”的原则，避免“select *” <br /><br />　　我们来做一个试验： <br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc <br /><br />　　用时：4673毫秒 <br /><br />select top 10000 gid,fariqi,title from tgongwen order by gid desc <br /><br />　　用时：1376毫秒 <br /><br />select top 10000 gid,fariqi from tgongwen order by gid desc <br /><br />　　用时：80毫秒 <br /><br />　　由此看来，我们每少提取一个字段，数据的提取速度就会有相应的提升。提升的速度<br />还要看您舍弃的字段的大小来判断。 <br /><br />　　10、count(*)不比count(字段)慢 <br /><br />　　某些资料上说：用*会统计所有列，显然要比一个世界的列名效率低。这种说法其实是<br />没有根据的。我们来看： <br /><br />select count(*) from Tgongwen <br /><br />　　用时：1500毫秒 <br /><br />select count(gid) from Tgongwen <br /><br />　　用时：1483毫秒 <br /><br />select count(fariqi) from Tgongwen <br /><br />　　用时：3140毫秒 <br /><br />select count(title) from Tgongwen <br /><br />　　用时：52050毫秒 <br /><br />　　从以上可以看出，如果用count(*)和用count(主键)的速度是相当的，而count(*)却比<br />其他任何除主键以外的字段汇总速度要快，而且字段越长，汇总的速度就越慢。我想，如<br />果用count(*)， SQL SERVER可能会自动查找最小字段来汇总的。当然，如果您直接写cou<br />nt(主键)将会来的更直接些。 <br /><br />　　11、order by按聚集索引列排序效率最高 <br /><br />　　我们来看：（gid是主键，fariqi是聚合索引列） <br /><br />select top 10000 gid,fariqi,reader,title from tgongwen <br /><br />　　用时：196 毫秒。 扫描计数 1，逻辑读 289 次，物理读 1 次，预读 1527 次。 <br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc <br /><br />　　用时：4720毫秒。 扫描计数 1，逻辑读 41956 次，物理读 0 次，预读 1287 次。 <br /><br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc <br /><br />　　用时：4736毫秒。 扫描计数 1，逻辑读 55350 次，物理读 10 次，预读 775 次。 <br /><br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc <br /><br />　　用时：173毫秒。 扫描计数 1，逻辑读 290 次，物理读 0 次，预读 0 次。 <br /><br />select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc <br /><br />　　用时：156毫秒。 扫描计数 1，逻辑读 289 次，物理读 0 次，预读 0 次。 <br /><br />　　从以上我们可以看出，不排序的速度以及逻辑读次数都是和“order by 聚集索引列”<br /> 的速度是相当的，但这些都比“order by 非聚集索引列”的查询速度是快得多的。 <br /><br />　　同时，按照某个字段进行排序的时候，无论是正序还是倒序，速度是基本相当的。 <br /><br /><br />　　12、高效的TOP <br /><br />　　事实上，在查询和提取超大容量的数据集时，影响数据库响应时间的最大因素不是数<br />据查找，而是物理的I/0操作。如： <br /><br />select top 10 * from ( <br /><br />select top 10000 gid,fariqi,title from tgongwen <br /><br />where neibuyonghu='办公室' <br /><br />order by gid desc) as a <br /><br />order by gid asc <br /><br />　　这条语句，从理论上讲，整条语句的执行时间应该比子句的执行时间长，但事实相反<br />。因为，子句执行后返回的是10000条记录，而整条语句仅返回 10条语句，所以影响数据<br />库响应时间最大的因素是物理I/O操作。而限制物理I/O操作此处的最有效方法之一就是使<br />用TOP关键词了。TOP关键词是 SQL SERVER中经过系统优化过的一个用来提取前几条或前几<br />个百分比数据的词。经笔者在实践中的应用，发现TOP确实很好用，效率也很高。但这个词<br />在另外一个大型数据库ORACLE中却没有，这不能说不是一个遗憾，虽然在ORACLE中可以用<br />其他方法（如：rownumber）来解决。在以后的关于“实现千万级数据的分页显示存储过程<br />”的讨论中，我们就将用到TOP这个关键词。 <br /><br />　　到此为止，我们上面讨论了如何实现从大容量的数据库中快速地查询出您所需要的数<br />据方法。当然，我们介绍的这些方法都是“软”方法，在实践中，我们还要考虑各种“硬<br />”因素，如：网络性能、服务器的性能、操作系统的性能，甚至网卡、交换机等。 <br /><br />三、实现小数据量和海量数据的通用分页显示存储过程 <br /><br />　　建立一个web 应用，分页浏览功能必不可少。这个问题是数据库处理中十分常见的问<br />题。经典的数据分页方法是:ADO 纪录集分页法，也就是利用ADO自带的分页功能（利用游<br />标）来实现分页。但这种分页方法仅适用于较小数据量的情形，因为游标本身有缺点：游<br />标是存放在内存中，很费内存。游标一建立，就将相关的记录锁住，直到取消游标。游标<br />提供了对特定集合中逐行扫描的手段，一般使用游标来逐行遍历数据，根据取出数据条件<br />的不同进行不同的操作。而对于多表和大表中定义的游标（大的数据集合）循环很容易使<br />程序进入一个漫长的等待甚至死机。 <br /><br />　　更重要的是，对于非常大的数据模型而言，分页检索时，如果按照传统的每次都加载<br />整个数据源的方法是非常浪费资源的。现在流行的分页方法一般是检索页面大小的块区的<br />数据，而非检索所有的数据，然后单步执行当前行。 <br /><br />　　最早较好地实现这种根据页面大小和页码来提取数据的方法大概就是“俄罗斯存储过<br />程”。这个存储过程用了游标，由于游标的局限性，所以这个方法并没有得到大家的普遍<br />认可。 <br /><br />　　后来，网上有人改造了此存储过程，下面的存储过程就是结合我们的办公自动化实例<br />写的分页存储过程： <br /><br />CREATE procedure pagination1 <br /><br />(@pagesize int,  --页面大小，如每页存储20条记录 <br /><br />@pageindex int   --当前页码 <br /><br />) <br /><br />as <br /><br />set nocount on <br /><br />begin <br /><br />declare @indextable table(id int identity(1,1),nid int)  --定义表变量 <br /><br />declare @PageLowerBound int  --定义此页的底码 <br /><br />declare @PageUpperBound int  --定义此页的顶码 <br /><br />set @PageLowerBound=(@pageindex-1)*@pagesize <br /><br />set @PageUpperBound=@PageLowerBound+@pagesize <br /><br />set rowcount @PageUpperBound <br /><br />insert into @indextable(nid) select gid from TGongwen where fariqi &gt;dateadd(da<br />y,-365,getdate()) order by fariqi desc <br /><br />select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t w<br />here O.gid=t.nid <br /><br />and t.id&gt;@PageLowerBound and t.id&lt;=@PageUpperBound order by t.id <br /><br />end <br /><br />set nocount off <br /><br />　　以上存储过程运用了SQL SERVER的最新技术――表变量。应该说这个存储过程也是一<br />个非常优秀的分页存储过程。当然，在这个过程中，您也可以把其中的表变量写成临时表<br />： CREATE TABLE #Temp。但很明显，在SQL SERVER中，用临时表是没有用表变量快的。所<br />以笔者刚开始使用这个存储过程时，感觉非常的不错，速度也比原来的ADO的好。但后来，<br />我又发现了比此方法更好的方法。 <br /><br />　　笔者曾在网上看到了一篇小短文《从数据表中取出第n条到第m条的记录的方法》，全<br />文如下： <br /><br />从publish 表中取出第 n 条到第 m 条的记录： <br />SELECT TOP m-n+1 * <br />FROM publish <br />WHERE (id NOT IN <br />　　　　(SELECT TOP n-1 id <br />　　　　 FROM publish)) <br /><br />id 为publish 表的关键字 <br /><br />　　我当时看到这篇文章的时候，真的是精神为之一振，觉得思路非常得好。等到后来，<br />我在作办公自动化系统（ASP.NET+ C#＋SQL SERVER）的时候，忽然想起了这篇文章，我想<br />如果把这个语句改造一下，这就可能是一个非常好的分页存储过程。于是我就满网上找这<br />篇文章，没想到，文章还没找到，却找到了一篇根据此语句写的一个分页存储过程，这个<br />存储过程也是目前较为流行的一种分页存储过程，我很后悔没有争先把这段文字改造成存<br />储过程： <br /><br />CREATE PROCEDURE pagination2 <br />( <br />@SQL nVARCHAR(4000),    --不带排序语句的SQL语句 <br />@Page int,              --页码 <br />@RecsPerPage int,       --每页容纳的记录数 <br />@ID VARCHAR(255),       --需要排序的不重复的ID号 <br />@Sort VARCHAR(255)      --排序字段及规则 <br />) <br />AS <br /><br />DECLARE @Str nVARCHAR(4000) <br /><br />SET @Str='SELECT   TOP '+CAST(@RecsPerPage AS VARCHAR(20))+' * FROM ('+@SQL+')<br /> T WHERE T.'+@ID+'NOT IN <br />(SELECT   TOP '+CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+' '+@ID+' FROM (<br />'+@SQL+') T9 ORDER BY '+@Sort+') ORDER BY '+@Sort <br /><br />PRINT @Str <br /><br />EXEC sp_ExecuteSql @Str <br />GO <br /><br />　　其实，以上语句可以简化为： <br /><br />SELECT TOP 页大小 * <br /><br />FROM Table1 <br /><br />WHERE (ID NOT IN <br /><br />         (SELECT TOP 页大小*页数 id <br /><br />        FROM 表 <br /><br />        ORDER BY id)) <br /><br />ORDER BY ID <br /><br />　　但这个存储过程有一个致命的缺点，就是它含有NOT IN字样。虽然我可以把它改造为<br />： <br /><br />SELECT TOP 页大小 * <br /><br />FROM Table1 <br /><br />WHERE not exists <br /><br />(select * from (select top (页大小*页数) * from table1 order by id) b where b.<br />id=a.id ) <br /><br />order by id <br /><br />　　即，用not exists来代替not in，但我们前面已经谈过了，二者的执行效率实际上是<br />没有区别的。 <br /><br />　　既便如此，用TOP 结合NOT IN的这个方法还是比用游标要来得快一些。 <br /><br />　　虽然用not exists并不能挽救上个存储过程的效率，但使用SQL SERVER中的TOP关键字<br />却是一个非常明智的选择。因为分页优化的最终目的就是避免产生过大的记录集，而我们<br />在前面也已经提到了TOP的优势，通过TOP 即可实现对数据量的控制。 <br /><br />　　在分页算法中，影响我们查询速度的关键因素有两点：TOP和NOT IN。TOP可以提高我<br />们的查询速度，而NOT IN会减慢我们的查询速度，所以要提高我们整个分页算法的速度，<br />就要彻底改造NOT IN，同其他方法来替代它。 <br /><br />　　我们知道，几乎任何字段，我们都可以通过max(字段)或min(字段)来提取某个字段中<br />的最大或最小值，所以如果这个字段不重复，那么就可以利用这些不重复的字段的max或m<br />in作为分水岭，使其成为分页算法中分开每页的参照物。在这里，我们可以用操作符“&gt;”<br />或“&lt;”号来完成这个使命，使查询语句符合SARG形式。如： <br /><br />Select top 10 * from table1 where id&gt;200 <br /><br />　　于是就有了如下分页方案： <br /><br />select top 页大小 * <br /><br />from table1 <br /><br />where id&gt; <br /><br />     (select max (id) from <br /><br />     (select top ((页码-1)*页大小) id from table1 order by id) as T <br /><br />      )     <br /><br /> order by id <br /><br />　　在选择即不重复值，又容易分辨大小的列时，我们通常会选择主键。下表列出了笔者<br />用有着1000万数据的办公自动化系统中的表，在以GID （GID是主键，但并不是聚集索引。<br />）为排序列、提取gid,fariqi,title字段，分别以第1、10、100、500、1000、1万、10 万<br />、25万、50万页为例，测试以上三种分页方案的执行速度：（单位：毫秒） <br /><br />页  码 <br />方案1 <br />方案2 <br />方案3 <br /><br />1 <br />60 <br />30 <br />76 <br /><br />10 <br />46 <br />16 <br />63 <br /><br />100 <br />1076 <br />720 <br />130 <br /><br />500 <br />540 <br />12943 <br />83 <br /><br />1000 <br />17110 <br />470 <br />250 <br /><br />1万 <br />24796 <br />4500 <br />140 <br /><br />10万 <br />38326 <br />42283 <br />1553 <br /><br />25万 <br />28140 <br />128720 <br />2330 <br /><br />50万 <br />121686 <br />127846 <br />7168 <br /><br /><br />　　从上表中，我们可以看出，三种存储过程在执行100页以下的分页命令时，都是可以信<br />任的，速度都很好。但第一种方案在执行分页1000页以上后，速度就降了下来。第二种方<br />案大约是在执行分页1万页以上后速度开始降了下来。而第三种方案却始终没有大的降势，<br />后劲仍然很足。 <br /><br />　　在确定了第三种分页方案后，我们可以据此写一个存储过程。大家知道SQL SERVER的<br />存储过程是事先编译好的SQL语句，它的执行效率要比通过WEB页面传来的SQL语句的执行效<br />率要高。下面的存储过程不仅含有分页方案，还会根据页面传来的参数来确定是否进行数<br />据总数统计。 <br /><br />-- 获取指定页的数据 <br /><br />CREATE PROCEDURE pagination3 <br /><br />@tblName   varchar(255),       -- 表名 <br /><br />@strGetFields varchar(1000) = '*',  -- 需要返回的列 <br /><br />@fldName varchar(255)='',      -- 排序的字段名 <br /><br />@PageSize   int = 10,          -- 页尺寸 <br /><br />@PageIndex  int = 1,           -- 页码 <br /><br />@doCount  bit = 0,   -- 返回记录总数, 非 0 值则返回 <br /><br />@OrderType bit = 0,  -- 设置排序类型, 非 0 值则降序 <br /><br />@strWhere  varchar(1500) = ''  -- 查询条件 (注意: 不要加 where) <br /><br />AS <br /><br />declare @strSQL   varchar(5000)       -- 主语句 <br /><br />declare @strTmp   varchar(110)        -- 临时变量 <br /><br />declare @strOrder varchar(400)        -- 排序类型 <br /><br /><br /><br />if @doCount != 0 <br /><br /> begin <br /><br />   if @strWhere !='' <br /><br />   set @strSQL = "select count(*) as Total from [" + @tblName + "] where "+@st<br />rWhere <br /><br />   else <br /><br />   set @strSQL = "select count(*) as Total from [" + @tblName + "]" <br /><br />end <br /><br />--以上代码的意思是如果@doCount传递过来的不是0，就执行总数统计。以下的所有代码都<br />是@doCount为0的情况 <br /><br />else <br /><br />begin <br /><br /><br /><br />if @OrderType != 0 <br /><br />begin <br /><br />   set @strTmp = "&lt;(select min" <br /><br />set @strOrder = " order by [" + @fldName +"] desc" <br /><br />--如果@OrderType不是0，就执行降序，这句很重要！ <br /><br />end <br /><br />else <br /><br />begin <br /><br />   set @strTmp = "&gt;(select max" <br /><br />   set @strOrder = " order by [" + @fldName +"] asc" <br /><br />end <br /><br /><br /><br />if @PageIndex = 1 <br /><br />begin <br /><br />   if @strWhere != ''   <br /><br />   set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "  from ["<br /> + @tblName + "] where " + @strWhere + " " + @strOrder <br /><br />    else <br /><br />    set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "  from [<br />"+ @tblName + "] "+ @strOrder <br /><br />--如果是第一页就执行以上代码，这样会加快执行速度 <br /><br />end <br /><br />else <br /><br />begin <br /><br />--以下代码赋予了@strSQL以真正执行的SQL代码 <br /><br />set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "  from [" <br /><br />   + @tblName + "] where [" + @fldName + "]" + @strTmp + "(["+ @fldName + "]) <br />from (select top " + str((@PageIndex-1)*@PageSize) + " ["+ @fldName + "] from <br />[" + @tblName + "]" + @strOrder + ") as tblTmp)"+ @strOrder <br /><br /><br /><br />if @strWhere != '' <br /><br />   set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "  from ["<br /><br /><br />       + @tblName + "] where [" + @fldName + "]" + @strTmp + "([" <br /><br />       + @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + "<br /> [" <br /><br />       + @fldName + "] from [" + @tblName + "] where " + @strWhere + " " <br /><br />       + @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder <br /><br />end <br /><br />end   <br /><br />exec (@strSQL) <br /><br />GO <br /><br />　　上面的这个存储过程是一个通用的存储过程，其注释已写在其中了。 <br /><br />　　在大数据量的情况下，特别是在查询最后几页的时候，查询时间一般不会超过9秒；而<br />用其他存储过程，在实践中就会导致超时，所以这个存储过程非常适用于大容量数据库的<br />查询。 <br /><br />　　笔者希望能够通过对以上存储过程的解析，能给大家带来一定的启示，并给工作带来<br />一定的效率提升，同时希望同行提出更优秀的实时数据分页算法。 <br /><br />四、聚集索引的重要性和如何选择聚集索引 <br /><br />　　在上一节的标题中，笔者写的是：实现小数据量和海量数据的通用分页显示存储过程<br />。这是因为在将本存储过程应用于“办公自动化”系统的实践中时，笔者发现这第三种存<br />储过程在小数据量的情况下，有如下现象： <br /><br />　　1、分页速度一般维持在1秒和3秒之间。 <br /><br />　　2、在查询最后一页时，速度一般为5秒至8秒，哪怕分页总数只有3页或30万页。 <br /><br />　　虽然在超大容量情况下，这个分页的实现过程是很快的，但在分前几页时，这个1－3<br />秒的速度比起第一种甚至没有经过优化的分页方法速度还要慢，借用户的话说就是“还没<br />有ACCESS数据库速度快”，这个认识足以导致用户放弃使用您开发的系统。 <br /><br />　　笔者就此分析了一下，原来产生这种现象的症结是如此的简单，但又如此的重要：排<br />序的字段不是聚集索引！ <br /><br />　　本篇文章的题目是：“查询优化及分页算法方案”。笔者只所以把“查询优化”和“<br />分页算法”这两个联系不是很大的论题放在一起，就是因为二者都需要一个非常重要的东<br />西――聚集索引。 <br /><br />　　在前面的讨论中我们已经提到了，聚集索引有两个最大的优势： <br /><br />　　1、以最快的速度缩小查询范围。 <br /><br />　　2、以最快的速度进行字段排序。 <br /><br />　　第1条多用在查询优化时，而第2条多用在进行分页时的数据排序。 <br /><br />　　而聚集索引在每个表内又只能建立一个，这使得聚集索引显得更加的重要。聚集索引<br />的挑选可以说是实现“查询优化”和“高效分页”的最关键因素。 <br /><br />　　但要既使聚集索引列既符合查询列的需要，又符合排序列的需要，这通常是一个矛盾<br />。 <br /><br />　　笔者前面“索引”的讨论中，将fariqi，即用户发文日期作为了聚集索引的起始列，<br />日期的精确度为“日”。这种作法的优点，前面已经提到了，在进行划时间段的快速查询<br />中，比用ID主键列有很大的优势。 <br /><br />　　但在分页时，由于这个聚集索引列存在着重复记录，所以无法使用max或min来最为分<br />页的参照物，进而无法实现更为高效的排序。而如果将ID主键列作为聚集索引，那么聚集<br />索引除了用以排序之外，没有任何用处，实际上是浪费了聚集索引这个宝贵的资源。 <br /><br />　　　为解决这个矛盾，笔者后来又添加了一个日期列，其默认值为getdate()。用户在写<br />入记录时，这个列自动写入当时的时间，时间精确到毫秒。即使这样，为了避免可能性很<br />小的重合，还要在此列上创建UNIQUE约束。将此日期列作为聚集索引列。 <br /><br />　　有了这个时间型聚集索引列之后，用户就既可以用这个列查找用户在插入数据时的某<br />个时间段的查询，又可以作为唯一列来实现max或min，成为分页算法的参照物。 <br /><br />　　经过这样的优化，笔者发现，无论是大数据量的情况下还是小数据量的情况下，分页<br />速度一般都是几十毫秒，甚至0毫秒。而用日期段缩小范围的查询速度比原来也没有任何迟<br />钝。 <br /><br />　　聚集索引是如此的重要和珍贵，所以笔者总结了一下，一定要将聚集索引建立在： <br /><br /><br />　　1、您最频繁使用的、用以缩小查询范围的字段上； <br /><br />　　2、您最频繁使用的、需要排序的字段上。 <br /><br />　　结束语： <br /><br />　　希望这篇文章不仅能够给大家的工作带来一定的帮助，也希望能让大家能够体会到分<br />析问题的方法；最重要的是，希望这篇文章能够抛砖引玉，掀起大家的学习和讨论的兴趣<br />，以共同促进。 <br />　　最后需要说明的是，在试验中，发现用户在进行大数据量查询的时候，对数据库速度<br />影响最大的不是内存大小，而是CPU。在我的P4 2.4机器上试验的时候，查看“资源管理器<br />”，CPU经常出现持续到100%的现象，而内存用量却并没有改变或者说没有大的改变。即使<br />在我们的HP ML 350 G3服务器上试验时，CPU峰值也能达到90%，一般持续在70%左右。 <br /><br />　　本文的试验数据都是来自我们的HP ML 350服务器。服务器配置：双Inter Xeon 超线<br />程 CPU 2.4G，内存1G，操作系统Windows Server 2003 Enterprise Edition，数据库SQL<br /> Server 2000 SP3。 <br /><br />转载完毕. <br /><br /><br />作者补充: <br />1.columns in('aa','bb') <br />他等于columns = 'aa' or columns ='bb' 他先去查询columns ='aa'放在一个临时的空间<br />里,然后等columns ='bb'查询完后,做个or查询得出结果. <br />至于效率的话，在columns建立索引的话, columns ='aa' or columns ='bb'要来的效率高<br /><br />语法分析器会将columns in('aa','bb')转化 <br />为columns ='aa' or columns ='bb'来执行。我们期望它会根据每个or子句分别查找，再<br />将结果 <br />相加，这样可以利用columns 上的索引；但实际上（根据showplan）,它却采用了"OR策略<br />" <br />，即先取出满足每个or子句的行，存入临时数据库的工作表中，再建立唯一索引以去掉 <br /><br />重复行，最后从这个临时表中计算结果。因此，实际过程没有利用columns 上索引，并且<br />完 <br />成时间还要受tempdb数据库性能的影响。 <br /><br /><br />2.效率从高到低 count(1)&gt;count(*)&gt;count([id]) <br /><br />3.select max(cols) from table1 的效率&gt;= select top 1 cols from table1 order by<br /> cols desc <br /><br />4.在where 做并列条件句时,where cols1='aa' and cols2='bb' <br />如果cols1 ='aa' 占95% cols2占5%的话，把cols2='bb'放在前面 ,因为他在检索cols ='<br />bb'的时候他只需查那5%,然后条件成立的话，去在这5%的纪录里 <br />去查找cols1 ='aa' <br /><br />5.避免用if条件句,可以用or来替代. <br />declare @vsql varchar(200) <br />set @vsql ='Renaski' <br />select * from titles where  @vsql ='Renaski' or price = 11.9500 <br /><br />如果@vsql为Renaski则把所有的纪录都选出来,如果不是的话，则只查询price = 11.9500<br /> 的纪录. <br /><br />6.任何对列的操作都将导致表扫描，它包括数据库函数、计算表达式等等，查询时 <br />要尽可能将操作移至等号右边。 <br /><br />7.尽量避免使用游标. <br />如果使用了游标，就要尽量避免在游标循环中再进行表连接的操作 <br /><br />8.取一个表的纪录数 <br />Select rows from sysindexes where id=object_id(N'titles') and indid&lt;2 <br />效率比 <br />select count(1) from titles来的高. <br /><br /><br />9.取的一个表的数据信息. <br />SELECT <br />表名=case when a.colorder=1 then d.name else '' end, <br />表说明=case when a.colorder=1 then isnull(f.value,'') else '' end, <br />字段序号=a.colorder, <br />字段名=a.name, <br />标识=case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' en<br />d, <br />主键=case when exists(SELECT 1 FROM sysobjects where xtype='PK' and name in ( <br /><br />SELECT name FROM sysindexes WHERE indid in( <br />SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid <br />))) then '√' else '' end, <br />类型=b.name, <br />占用字节数=a.length, <br />长度=COLUMNPROPERTY(a.id,a.name,'PRECISION'), <br />小数位数=isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0), <br />允许空=case when a.isnullable=1 then '√'else '' end, <br />默认值=isnull(e.text,''), <br />字段说明=isnull(g.[value],''), <br />索引名称=isnull(h.索引名称,''), <br />索引顺序=isnull(h.排序,'') <br />FROM syscolumns a <br />left join systypes b on a.xtype=b.xusertype <br />inner join sysobjects d on a.id=d.id and d.xtype='U' and d.status&gt;=0 <br />left join syscomments e on a.cdefault=e.id <br />left join sysproperties g on a.id=g.id and a.colid=g.smallid <br />left join sysproperties f on d.id=f.id and f.smallid=0 <br />left join(--这部分是索引信息,如果要显示索引与表及字段的对应关系,可以只要此部分<br /><br />select 索引名称=a.name,c.id,d.colid <br />,排序=case indexkey_property(c.id,b.indid,b.keyno,'isdescending') <br />when 1 then '降序' when 0 then '升序' end <br />from sysindexes a <br />join sysindexkeys b on a.id=b.id and a.indid=b.indid <br />join (--这里的作用是有多个索引时,取索引号最小的那个 <br />select id,colid,indid=min(indid) from sysindexkeys <br />group by id,colid) b1 on b.id=b1.id and b.colid=b1.colid and b.indid=b1.indid <br /><br />join sysobjects c on b.id=c.id and c.xtype='U' and c.status&gt;=0 <br />join syscolumns d on b.id=d.id and b.colid=d.colid <br />where a.indid not in(0,255) <br />) h on a.id=h.id and a.colid=h.colid <br />--where d.name='要查询的表' --如果只查询指定表,加上此条件 <br />order by a.id,a.colorder <br /><br /><br />10.创建一个表结构. <br />select * into #b from authors where 1=2; <br />注意: <br />#table1 <br />##table1 <br />@table1 <br /><br />局部临时表 <br />以一个井号（#）开头的那些表名。只有在创建本地临时表的连接上才能看到这些表。 <br /><br />全局临时表 <br />以两个井号（##）开头的那些表名。在所有连接上都能看到全局临时表。如果在创建全局<br />临时表的连接断开前没有显式地除去这些表，那么只要所有其它任务停止引用它们，这些<br />表即被除去。当创建全局临时表的连接断开后，新的任务不能再引用它们。当前的语句一<br />执行完，任务与表之间的关联即被除去；因此通常情况下，只要创建全局临时表的连接断<br />开，全局临时表即被除去。 <br /><br />@和#有和不同：@@在内存，#在硬盘。我的体会是只要方便且数据量不大，使用@@。 <br /><br />11.视图 <br />他只是记住要连接,关联列的信息,他不存放任何物理数据. <br />在调用的时候他还是去取各个表中的数据. <br /><br />12.尽量不要用text属性 <br />系统为他专门开辟一个空间来存放. <br />用t-sql/varchar替代 <br />pl/sql  varchar2 替代. <br /><br />13 <br />GO语句是个命令识别并通过osql和isql和SQL 查询分析器非T-SQL语句进行识别。 <br />如果你使用查询分析器作为你的主开发工具，其他语句和库文件将不会识别GO语句作为一<br />个T-SQL命令 <br /><br />14. <br />用exec 效率来的高. <br />declare @sql nvarchar(300) <br />  set @sql='select * from titles' <br />execute sp_executesql @sql <br /><br />15,注意你的tempdb,使他自动增长. <br /><br />16 使用no_log <br />select * from titles no_logs <br /><br />17去不重复纪录时,尽量用dictinct <br /><br />18.尽量避免反复访问同一张或几张表，尤其是数据量较大的表，可以考虑先根据条件提取<br />数据到临时表中，然后再做连接。 <br /><br /><br />19 尽量使用“&gt;=”，不要使用“&gt;”。 他会找到某个确定的数字进行筛选,而&gt;则没有. <br /><br /><br />20注意表之间连接的数据类型，避免不同类型数据之间的连接。 <br /><br />21.可用ASE调优命令：set statistics io on, set statistics time on , set showpla<br />n on 等,进行优化 <br /><br />22.truncate table 删除数据 <br />而不是delete from table <br /><br /><br />三.死锁 <br /><br />像SQL server一样的关系数据库使用锁来防止用户“互相踩到对方的脚趾头”。也就是说<br />，锁可以防止用户造成修改数据时的碰撞。当一个用户锁住一段代码时候，其它的用户都<br />不能修改这段数据。另外，一个锁阻止了用户观看未被授权的数据修改。用户必须等待到<br />数据修改并保存之后才能够查看它。数据必须使用不同的方法来加锁。SQL Server 2000使<br />用锁来实现多用户同时修改数据库同一数据时的同步控制 <br />如果数据量超过200个数据页面（400k），那么系统将会进行锁升级，页级锁会升级成表级<br />锁。 　　 <br /><br /><br />死锁 <br />一个数据库的死锁是发生在两个或多于两个访问一些资源的数据库会话中的，并且这些会<br />话相互之间有依赖关系。死锁是可以在任意一个多线程的系统成出现的一个情况，不仅仅<br />局限于关系数据库管理系统。一个多线程系统中的线程可能需要一个或多个资源(例如，锁<br />)。如果申请的资源正在被另外一个线程所使用，那么第一个线程就需要等待持有该资源的<br />线程的释放它所需要的资源。假设等待线程持有一个那个正拥有线程所依赖的资源。下面<br />的这一段代码就可以造成死锁异常现象的发生： <br />System.Data.SqlClient.SqlException: Transaction (Process ID 12) was deadlocked<br /> on lock resources with another process and has been chosen as the deadlock vi<br />ctim. Rerun the transaction. <br /><br />当一个SQL Server的调用和另外一个资源发生冲突时就会抛出异常，这个资源持有一个必<br />要的资源。结果是，一个进程就被终止了。当进程的ID号成为系统的唯一标识的时候，这<br />会是一个很平常死锁的消息错误。 <br /><br /><br />锁的类型 <br />一个数据库系统在许多情况下都有可能锁数据项。其可能性包括： <br /><br />Rows—数据库表中的一整行 <br />Pages—行的集合（通常为几kb） <br />Extents—通常是几个页的集合 <br />Table—整个数据库表 <br />Database—被锁的整个数据库表 <br /><br />除非有其它的说明，数据库根据情况自己选择最好的锁方式。不过值得感谢的是，SQL Se<br />rver提供了一种避免默认行为的方法。这是由锁提示来完成的。 <br /><br /><br />提示 <br />或许你有过许多如下的经历：需要重设SQL Server的锁计划，并且加强数据库表中锁范围<br />。Tansact－SQL提供了一系列不同级别的锁提示，你可以在SELECT,INSERT, UPDATE和DEL<br />ETE中使用它们来告诉SQL Server你需要如何通过重设任何的系统或事务级别来锁表格。可<br />以实现的提示包括： <br /><br />FASTFIRSTROW—选取结果集中的第一行，并将其优化 <br />HOLDLOCK—持有一个共享锁直至事务完成 <br />NOLOCK—不允许使用共享锁或独享锁。这可能会造成数据重写或者没有被确认就返回的情<br />况；因此，就有可能使用到脏数据。这个提示只能在SELECT中使用。 <br />PAGLOCK—锁表格 <br />READCOMMITTED—只读取被事务确认的数据。这就是SQL Server的默认行为。 <br />READPAST—跳过被其它进程锁住的行，所以返回的数据可能会忽略行的内容。这也只能在<br />SELECT中使用。 <br />READUNCOMMITTED—等价于NOLOCK. <br />REPEATABLEREAD—在查询语句中，对所有数据使用锁。这可以防止其它的用户更新数据，<br />但是新的行可能被其它的用户插入到数据中，并且被最新访问该数据的用户读取。 <br />ROWLOCK—按照行的级别来对数据上锁。SQL Server通常锁到页或者表级别来修改行，所以<br />当开发者使用单行的时候，通常要重设这个设置。 <br />SERIALIZABLE—等价于HOLDLOCK. <br />TABLOCK—按照表级别上锁。在运行多个有关表级别数据操作的时候，你可能需要使用到这<br />个提示。 <br />UPDLOCK—当读取一个表的时候，使用更新锁来代替共享锁，并且保持一直拥有这个锁直至<br />事务结束。它的好处是，可以允许你在阅读数据的时候可以不需要锁，并且以最快的速度<br />更新数据。 <br />XLOCK—给所有的资源都上独享锁，直至事务结束。 <br /><br />对于数据库死锁，通常可以通过TRACE FLAG 1204、1205、1206，检查ERRORLOG里面的输出<br />，和分析SQLTRACE的执行上下文判断死锁问题的来由。 <br />TRACEON函数的第三个参数设置为-1，表示不单单针对当前connection，而是针对所有包括<br />未来建立的connection。这样，才够完全，否则只是监视当前已经建立的数据库连接了。<br /><br /><br /><br />执行下面的话可以把死锁记录到Errorlog中： <br /><br />dbcc traceon (1204, 3605, -1) <br />go <br />dbcc tracestatus(-1) <br />go <br /><br />  <br /><br />得到的输出为： <br />DBCC 执行完毕。如果 DBCC 输出了错误信息，请与系统管理员联系。 <br />TraceFlag Status <br />--------- ------ <br />1204      1 <br />1205      1 <br />3605      1 <br /><br />（所影响的行数为 3 行） <br /><br />DBCC 执行完毕。如果 DBCC 输出了错误信息，请与系统管理员联系。 <br /><br /><br />此后，你可以查看数据库的例行日志，每隔一段时间，数据库都会检查死锁 <br />2004-01-16 18:34:38.50 spid4     ---------------------------------- <br />2004-01-16 18:34:38.50 spid4     Starting deadlock search 1976 <br /><br /><br /><br />2004-01-16 18:34:38.50 spid4     Target Resource Owner: <br />2004-01-16 18:34:38.50 spid4      ResType:LockOwner Stype:'OR' Mode: U SPID:55<br /> ECID:0 Ec:(0xAA577570) Value:0x4c25cba0 <br />2004-01-16 18:34:38.50 spid4      Node:1  ResType:LockOwner Stype:'OR' Mode: U<br /> SPID:55 ECID:0 Ec:(0xAA577570) Value:0x4c25cba0 <br />2004-01-16 18:34:38.50 spid4      Node:2  ResType:LockOwner Stype:'OR' Mode: U<br /> SPID:71 ECID:0 Ec:(0xABF07570) Value:0x9bd0ba00 <br />2004-01-16 18:34:38.50 spid4     <br />2004-01-16 18:34:38.50 spid4     -- next branch -- <br />2004-01-16 18:34:38.50 spid4      Node:2  ResType:LockOwner Stype:'OR' Mode: U<br /> SPID:71 ECID:0 Ec:(0xABF07570) Value:0x9bd0ba00 <br />2004-01-16 18:34:38.50 spid4     <br />2004-01-16 18:34:38.50 spid4     <br />2004-01-16 18:34:38.50 spid4     End deadlock search 1976 ... a deadlock was n<br />ot found. <br />2004-01-16 18:34:38.50 spid4     ---------------------------------- <br /><br />DBCC TRACEON打开（启用）指定的跟踪标记。 <br /><br />注释跟踪标记用于自定义某些控制 Microsoft? SQL Server? 操作方式的特性。跟踪标记<br />在服务器中一直保持启用状态，直到通过执行 DBCC TRACEOFF 语句对其禁用为止。在发出<br /> DBCC TRACEON 语句之前，连入到服务器的新连接看不到任何跟踪标记。一旦发出该语句<br />，该连接就能看到服务器中当前启用的所有跟踪标记（即使这些标记是由其它连接启用）<br />。 <br />跟踪标记跟踪标记用于临时设置服务器的特定特征或关闭特定行为。如果启动 Microsoft<br />? SQL Server 时设置了跟踪标记 3205，将禁用磁带驱动程序的硬件压缩。跟踪标记经常<br />用于诊断性能问题，或调试存储过程或复杂的计算机系统。 <br />下列跟踪标记在 SQL Server 中可用。跟踪标记 描述 1204 返回参与死锁的锁的类型以及<br />当前受影响的命令。 <br />实际上可以在“错误 1000 -1999”中找到他们： <br />1204 19 SQL Server 此时无法获取 LOCK 资源。请在活动用户数较少时重新运行您的语句<br />，或者请求系统管理员检查 SQL Server 锁和内存配置。 <br />1205 13 事务（进程 ID %1!）与另一个进程已被死锁在资源 {%2!} 上，且该事务已被选<br />作死锁牺牲品。请重新运行该事务。 <br />1206 18 事务管理器已取消了分布式事务。 <br /><br />需要指出的是对锁的升级,完全是由系统自行判断的,而非人为.如果要避免死锁的话，其根<br />本还在与数据库的设计上<br /></p>
<img src ="http://www.blogjava.net/weibogao/aggbug/93392.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weibogao/" target="_blank">weibogao</a> 2007-01-12 12:56 <a href="http://www.blogjava.net/weibogao/archive/2007/01/12/93392.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转《数据库设计经验》</title><link>http://www.blogjava.net/weibogao/archive/2007/01/11/93125.html</link><dc:creator>weibogao</dc:creator><author>weibogao</author><pubDate>Thu, 11 Jan 2007 04:09:00 GMT</pubDate><guid>http://www.blogjava.net/weibogao/archive/2007/01/11/93125.html</guid><wfw:comment>http://www.blogjava.net/weibogao/comments/93125.html</wfw:comment><comments>http://www.blogjava.net/weibogao/archive/2007/01/11/93125.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weibogao/comments/commentRss/93125.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weibogao/services/trackbacks/93125.html</trackback:ping><description><![CDATA[
		<h3 class="title"> </h3>
		<p>2004-11-24微软技术博客 </p>
		<p>作者： wjk.net(转载)</p>
		<p>一个成功的管理系统，是由：[50% 的业务 + 50% 的软件] 所组成，而 50% 的成功软件又有 [25% 的数据库 + 25%的程序] 所组成，数据库设计的好坏是一个关键。如果把企业的数据比做生命 所必需的血液，那么数据库的设计就是应用中最重要的一部分 。有关数据库设计的材料汗牛充栋，大学学位课程里也有专门的讲述 。不过，就如我们反复强调的那样，再好的老师也比不过经验的教诲 。所以我归纳历年来所走的弯路及体会，并在网上找了些对数据库设 计颇有造诣的专业人士给大家传授一些设计数据库的技巧和经验 。精选了其中的60 个最佳技巧，并把这些技巧编写成了本文，为了方便索引其内容划分 为 5 个部分：</p>
		<p>
				<br />
		</p>
		<p>第 1 部分 - 设计数据库之前 </p>
		<p>这一部分罗列了 12 个基本技巧，包括命名规范和明确业务需求等。 </p>
		<p>第 2 部分 - 设计数据库表 </p>
		<p>总共 24 个指南性技巧，涵盖表内字段设计以及应该避免的常见问题等。 </p>
		<p>第 3 部分 - 选择键 </p>
		<p>怎么选择键呢？这里有 10 个技巧专门涉及系统生成的主键的正确用法，还有何 时以及如何索引字段以获得最佳性能等。 </p>
		<p>第 4 部分 - 保证数据完整性 </p>
		<p>讨论如何保持数据库的清晰和健壮，如何把有害数据降低到最小程度 。 </p>
		<p>第 5 部分 - 各种小技巧 </p>
		<p>不包括在以上 4 个部分中的其他技巧，五花八门，有了它们希望你的数据库开发工作 会更轻松一些。 </p>
		<p>
				<br />第 1 部分 - 设计数据库之前 </p>
		<p>
				<br />考察现有环境 </p>
		<p>在设计一个新数据库时，你不但应该仔细研究业务需求而且还要考察 现有的系统。大多数数据库项目都不是从头开始建立的；通常 ，机构内总会存在用来满足特定需求的现有系统（可能没有实现自动 计算）。显然，现有系统并不完美，否则你就不必再建立新系统了 。但是对旧系统的研究可以让你发现一些可能会忽略的细微问题 。一般来说，考察现有系统对你绝对有好处。 </p>
		<p>定义标准的对象命名规范 </p>
		<p>一定要定义数据库对象的命名规范。对数据库表来说 ，从项目一开始就要确定表名是采用复数还是单数形式 。此外还要给表的别名定义简单规则（比方说，如果表名是一个单词 ，别名就取单词的前4 个字母；如果表名是两个单词，就各取两个单词的前两个字母组成 4 个字母长的别名；如果表的名字由 3个单词组成，你不妨从头两个单词中各取一个然后从最后一个单词中 再取出两个字母，结果还是组成 4字母长的别名，其余依次类推）对工作用表来说，表名可以加上前缀 WORK_后面附上采用该表的应用程序的名字。表内的列[字段 ]要针对键采用一整套设计规则。比如，如果键是数字类型 ，你可以用 _N作为后缀；如果是字符类型则可以采用 _C后缀。对列[字段]名应该采用标准的前缀和后缀。再如 ，假如你的表里有好多"money"字段，你不妨给每个列[字段 ]增加一个 _M后缀。还有，日期列[字段]最好以 D_ 作为名字打头。</p>
		<p>检查表名、报表名和查询名之间的命名规范。你可能会很快就被这些 不同的数据库要素的名称搞糊涂了。假如你坚持统一地命名这些数据 库的不同组成部分，至少你应该在这些对象名字的开头用Table、Query 或者 Report 等前缀加以区别。</p>
		<p>如果采用了 Microsoft Access，你可以用 qry、rpt、tbl 和 mod 等符号来标识对象（比如tbl_Employees）。我在和 SQL Server 打交道的时候还用过 tbl 来索引表，但我用 sp_company （现在用 sp_feft_）标识存储过程，因为在有的时候如果我发现了更 好的处理办法往往会保存好几个拷贝。我在实现 SQL Server 2000 时用udf_ （或者类似的标记）标识我编写的函数。 </p>
		<p>
				<br />工欲善其事, 必先利其器 </p>
		<p>采用理想的数据库设计工具，比如：SyBase 公司的 PowerDesign，她支持 PB、VB、Delphe 等语言，通过 ODBC可以连接市面上流行的 30 多个数据库，包括 dBase、FoxPro、VFP、SQL Server 等，今后有机会我将着重介绍PowerDesign 的使用。 </p>
		<p>获取数据模式资源手册 </p>
		<p>正在寻求示例模式的人可以阅读《数据模式资源手册》一书，该书由 Len Silverston、W. H. Inmon 和 Kent Graziano 编写，是一本值得拥有的最佳数据建模图书。该书包括的章节涵盖多 种数据领域，比如人员、机构和工作效能等。其他的你还可以参考： [1]萨师煊王珊著数据库系统概论(第二版)高等教育出版社 1991、[2][美] Steven M.Bobrowski 著 Oracle 7<br />与客户／服务器计算技术从入门到精通刘建元等译电子工业出版社，1996、[3]周中元信息系统建模方法(下)电子与信息化1999年第3期，1999畅想未来，但不可忘了过去的教训我发现询问用户如何看待未来需求变化非常有用。这样做可以达到两 个目的：首先，你可以清楚地了解应用设计在哪个地方应该更具灵活 性以及如何避免性能瓶颈；其次，你知道发生事先没有确定的需求变 更时用户将和你一样感到吃惊。</p>
		<p>一定要记住过去的经验教训！我们开发人员还应该通过分享自己的体 会和经验互相帮助。即使用户认为他们再也不需要什么支持了 ，我们也应该对他们进行这方面的教育，我们都曾经面临过这样的时 刻"当初要是这么做了该多好.."。 </p>
		<p>在物理实践之前进行逻辑设计 </p>
		<p>在深入物理设计之前要先进行逻辑设计。随着大量的 CASE工具不断涌现出来，你的设计也可以达到相当高的逻辑水准 ，你通常可以从整体上更好地了解数据库设计所需要的方方面面。 </p>
		<p>了解你的业务 </p>
		<p>在你百分百地确定系统从客户角度满足其需求之前不要在你的 ER（实体关系）模式中加入哪怕一个数据表（怎么，你还没有模式 ？那请你参看技巧9）。了解你的企业业务可以在以后的开发阶段节约大量的时间 。一旦你明确了业务需求，你就可以自己做出许多决策了。</p>
		<p>一旦你认为你已经明确了业务内容，你最好同客户进行一次系统的交 流。采用客户的术语并且向他们解释你所想到的和你所听到的 。同时还应该用可能、将会和必须等词汇表达出系统的关系基数 。这样你就可以让你的客户纠正你自己的理解然后做好下一步的ER 设计。 </p>
		<p>创建数据字典和 ER 图表 </p>
		<p>一定要花点时间创建 ER 图表和数据字典。其中至少应该包含每个字段的数据类型和在每个表 内的主外键。创建 ER<br />图表和数据字典确实有点费时但对其他开发人员要了解整个设计却是 完全必要的。越早创建越能有助于避免今后面临的可能混乱 ，从