<?xml version="1.0" encoding="gb2312"?>

<!-- RSS generated by oioj.net on 4/16/2004 ; 感谢LeXRus提供 RSS 2.0 文档; 此文件可自由使用，但请保留此行信息 --> 
<!-- Source download URL: http://blogger.org.cn/blog/rss2.asp       -->
<rss version="2.0">

<channel>
<title>技术至上</title>
<link>http://blogger.org.cn/blog/blog.asp?name=fengxin</link>
<description>Stanley的博客</description>
<copyright>blogger.org.cn</copyright>
<generator>W3CHINA Blog</generator>
<webMaster>webmaster@blogger.org.cn</webMaster>
<item>
<title><![CDATA[关于“WAPI”的一些资料]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=fengxin&amp;id=4442</link>
<author>fengxin</author>
<pubDate>2005/4/3 14:56:51</pubDate>
<description><![CDATA[
<P><STRONG><FONT face=楷体_GB2312 color=#1919f8 size=5>资料1：</FONT></STRONG></P>
<P><FONT color=#ff0000>名词解释：什么是WAPI？（中国无线局域网安全强制性标准）</FONT> </P>
<P style="TEXT-INDENT: 2em">WAPI是WLAN Authentication and Privacy Infrastructure的英文缩写。它像红外线、蓝牙、GPRS、CDMA1X等协议一样，是无线传输协议的一种，只不过跟它们不同的是它是无线局域网（WLAN）中的一种传输协议而已，它与现行的802.11B传输协议比较相近。 
<P style="TEXT-INDENT: 2em">那么，为什么制定传输协议的标准呢？我们知道，不同的传输协议将数据包在两台以上的电子设备间进行传输所用的原理和实现的手段是不同的，它们多数都不兼容，如果不制定无线传输协议的标准的话，无线电子设备的通用性就会受到很大的限制，例如，你的笔记本电脑在A地方也许可以无线上网，但去到了B地方，可能就会由于传输协议不统一而无法实现无线上网了，而如果所有的无线产品都使用同一种传输协议的话，那么，你的笔记本电脑无论走到哪里，只要有WLAN信号的地方都可以轻松实现无线上网了。 </P>
<P style="TEXT-INDENT: 2em"><BR><FONT size=5><FONT face=楷体_GB2312 color=#1919f8><STRONG>资料2：<BR><BR></STRONG></FONT></FONT><FONT face=宋体 color=#f70938 size=3>中美谈判结果揭晓,WAPI无限延期</FONT></P>
<P style="TEXT-INDENT: 2em">中美两国于美国当地时间星期三宣布，两国经过谈判，已就广泛的贸易与技术问题达成协定。根据协定，中方将缓和强制建立自己的Wi-Fi安全标准的做法，并且将采取更加严厉的防止盗版的政策。</P>
<P>　　据了解谈判过程的消息灵通人士透露，中方同意美方提出的要求，不在6月1日最后期限到来之时强制实施WAPI技术标准，并将无限期推迟实施WAPI技术标准的时间。与此同时，中方将与国际标准组织IEEE(电气与电子工程师协会)协作，对WAPI技术标准进行修改和完善。</P>
<P>　　根据双方达成的版权保护协定，中方将开展旨在打击以CD和其他载体为媒介的盗版行为的活动、支持司法机关对盗版者提起刑事诉讼并努力使中国的法律与国际间防止互联网盗版的版权保护公约接轨。</P>
<P>　　星期三宣布的协定有助于缓解中美之间因科技问题而出现紧张的商贸气氛。例如，WAPI问题就是此次中美之间爆发商贸争端的一个核心问题。</P>
<P style="TEXT-INDENT: 2em"><STRONG><FONT face=楷体_GB2312 color=#1919f8 size=5><BR>资料3：</FONT></STRONG></P>
<DIV class=ftitle><FONT color=#f70938>学者新论：推迟WAPI并非迁就美国</FONT><FONT class=fbody id=zoom></DIV>
<DIV id=p_content>
<P>　　在今年四月下旬的中美商务会议上，双方达成协议，其中一条是中国无限期推迟WAPI标准的实施。对此，众说纷纭。笔者认为，中国政府的决定完全正确，许多疑问大都是由于报刊媒体的不实报道和对知识产权的误解而产生的；重要的事情，不是纠缠WAPI推迟是否是贸易游戏，而是从这个标准的制定和推行中汲取教训。<BR><BR>　　<B>报刊媒体的不实报道</B><BR><BR>　　一些报刊媒体报道说，WAPI的椭圆曲线加密技术是“自主知识产权”的产品，颇有填补空白的味道。实际情况是，早在十年前左右，即1994年前后，美国就搞定了椭圆曲线加密技术、并形成了“X9.X”和“186-2”DSS系列，在以后的具体使用方面，取得了许多技术和法律上的进一步完善。由于WAPI没有公开技术具体内容，公众就不能断定它的椭圆曲线加密技术到底有多少是自主的创造发明、有多少是照搬仿制别人的技术发明。尽管如此，从WAPI已经给出的资料看，其技术水平没有超出“X9.X”和“186-2”DSS系列的水平。例如，“X9.X”使用随机密钥，而WAPI使用身份证作密钥。众所周知，固定密钥比随机密钥的功能效率要差出许多，而且，在网络上使用身份证和信用卡等个人资料作密钥密码是安全技术中最忌讳的选择之一。<BR><BR>　　一些报刊媒体报道说，WAPI是按照IEEE组织的802.11标准设计制作的，似乎得到IEEE组织的认可；同时又说，WAPI填补了802.11的安全漏洞。实际情况是，在2003年11月中旬的IEEE组织会议上，也在该组织2003年11月23日给中国国家标准局和中国信息产业部的回信中，IEEE表示拒绝WAPI，其要点是：<BR><BR>　　——WAPI类似（不是“符合”）802.11标准，但不符合IEEE标准对个人资料和隐私的保护要求；<BR><BR>　　——WAPI增加使用成本，不符合802.11标准对厂商和用户双方都降低使用成本的要求；<BR><BR>　　——WAPI不符合全球通用的要求，因此，IEEE组织质疑WAPI成为国际标准一部分的要求；---&nbsp;802.11标准不是完美的，但IEEE组织已经开始起草802.11i等系列来完善它；<BR><BR>　　——与此同时，IEEE的802.16标准工作组已经受到邀请，将出席2004年5月17日在中国深圳举行的“802”标准系列研讨会，欢迎中国标准局和中国信息部派高层人员参加，共同商讨局域网标准技术问题和中国在IEEE组织发挥作用的问题。<BR><BR>　　很明显，WAPI并非象报刊媒体报道说的那么“神”。一些报道说，为了补偿WAPI无限期推迟、中美将举行会议讨论WAPI修改问题。那样报道是不确切的，实际情况是，早在年底年初、即宣布无限期推迟WAPI之前，两国之间就已经有所安排了。<BR><BR>　　由于报刊媒体报道评论把WAPI推上了高峰，----它是自主知识产权的，它是关系到中国在局域网市场的洗牌机会的，是为国家安全而实行的强制性标准，等等，----因而，在社会上产生了错觉，好象这个标准的实施将有相当重大的意义，支持WAPI就是符合国家利益、反对WAPI就是反对国家利益。在这种错觉影响下，很自然，听到无限期推迟WAPI标准的消息，有些人就以为那是“以让步换高科技产品”的贸易游戏，有的则说“推迟WAPI不是放弃”或“WAPI标准之争犹存变数”，甚至发生了对中国政府的决定是否过于迁就美国的疑问。<BR><BR>　　<B>WAPI标准的几点教训</B><BR><BR>　　笔者认为，中国政府决定无限期推迟WAPI，并非是迁就美国，而是纠正了一个可能发生种种不利于中国、也不利于国际社会的不妥做法，体现了现政府多次提出的求真务实、依法治国和公开透明的政策主张的特点，为业界和相关政府部门在实质上而不是在形式上反省自己的工作思路，提供了一个具体案例。<BR><BR>　　（一）&nbsp;&nbsp;要按照“依法治国”的精神管理科技发展和国家标准，要讲究成本效益。胡锦涛主席多次指出，要求真务实和依法治国。且不管外国企业和外国政府如何看待，中国自己的标准法就包括和明确规定：<BR><BR>　　*&nbsp;制定标准应当有利于保护消费者的利益。<BR><BR>　　*&nbsp;制定标准应当有利于推广科技成果，提高经济效益，有利于产品的通用互换，做到技术先进、经济合理。<BR><BR>　　*&nbsp;制定标准应当做到有关标准的协调配套。<BR><BR>　　*&nbsp;制定标准应当有利于促进对外经济技术合作和对外贸易。<BR><BR>　　就截止目前的情况看，WAPI标准制定和推行没有满足国家标准法的要求，没有充分考虑消费者利益、经济效益、协调配套、通用性、对外合作和对外贸易。例如，WAPI实行起来，局域网及其设备只能在中国市场使用、而且只能在使用授权企业产品的情况下才行得通，这就等于是让极少数企业获得巨额利润，其代价是把中国的局域网封闭成一个“世界孤岛”、迫使成千上万的中国消费者为这个孤岛的生存承担所有费用。如果按照这种思路发展中国信息产业，那么，海外相关技术产品难以进口、中国技术产品难以出口，中国的计算机互联网发展就很可能成为一个庞大的和与世隔绝的“内部网”市场。此外，报刊媒体说，WAPI的制定和推行依据的是商业密码产品保密法。然而，那个法规是规定如何在某个特别领域实行保密，并不是规定如何制定和推行国家标准。换句话说，WAPI国家标准的制定和推行，没有充分的法律依据。<BR><BR>　　（二）&nbsp;&nbsp;要树立开放共享的发展思路。温家宝总理指出，中国的科技发展，“要面向世界、面向未来，搞开放式的研究，要有国际视野。”信息时代发展的一个重要特征，就是全球性的开放共享。温总理提出的政策主张完全符合历史发展趋势和时代要求。<BR><BR>　　然而，迄今为止，WAPI还没有公开它的技术内容和论证，也没有公开说明它的成本效益、而这是国家标准法对制定和推行国家标准的基本要求之一。WAPI的做法，让人看到了微软公司的影子。例如，互联网普及没多久，JAVA编程语言开始流行，其主要特点之一就是兼容性高、开放共享倾向很强。为了保护自己的地盘，微软公司抓住JAVA尚未完善或正在完善的地方，搞了模仿版本，使其不兼容、也不开放共享，走的是封闭捆绑的道路。经过一段时间市场考验，证明微软公司的道路行不通。现在，该公司的思路成了计算机互联网发展主流的局外人。WAPI的制定和推行，几乎类似，不同的仅仅是具体产品对象。这样的做法提出了一个问题：为中国信息产业发展制定政策和标准的具体部门机构，是如何理解时代发展的特征和趋向的？微软公司的思路是中国信息产业发展的思路吗？相关部门机构是否应该认真整顿一下工作思路和工作框架了呢？<BR><BR>　　（三）&nbsp;&nbsp;要正确和全面地理解知识产权保护。WAPI的制定和推行，反映了政府部门、报刊媒体和社会上的一些人对专利和国家标准的关系、私人和公共知识产权的关系等方面的片面理解。国家标准属于全体公民的公共知识，其产权由特定政府机构代表全国公民实施管理，因而，不管标准参与者是谁，国家标准的制定和推行必须向全体公民开放，公民可以随时随地地了解情况和免费使用公共知识。而专利则属于私人或私人机构的知识产权，它是否开放、如何管理和是否免费或收费，均由产权拥有者决定；更重要的是，私人专利不是法规，别人可以完全拒绝、甚至根本不予理会，而国家标准具有法规性质，所有公民都必须尊从。这些，是私人专利知识产权和国家标准公共知识产权之间的重要不同点。<BR><BR>　　进一步说，企业和私人可以参与国家标准的制定，但国家标准还是属于公共知识产权，并非因为有私人和企业参与就成了为少数公民或少数企业利益服务的工具。而WAPI的制定和推行，混淆了公共所有和私人所有的区别：说它是国家标准，却不向所有公民公开，有的报刊媒体则干脆说WAPI是项企业专利；说它是私人或某企业的专利，却又以国家标准的名义强迫全体公民接受。这种做法，实质上是把国家标准当作部分公民获利的工具、或是把部分公民获利的专利充作国家标准来强迫全体公民接受和为其付款，都是典型的利益冲突。改革开放政策的重点内容之一，就是政企分家。如何解决公私不分和政企不分的利益冲突问题，越来越成为中国发展和深化改革开放的和亟待解决的重大问题之一，而WAPI又提供了一个现实的教训。<BR><BR>　　诚然，在现代科技市场，谁掌握了标准、谁就有主动权。然而，如果没有市场和使用者（特别是大众消费者）的广泛支持，这个主动权就是个空架子。制定和推行国家标准是政府行为、不是私人行为或企业行为，因而必须执法为民，必须为全体公民的利益服务、而不是为少数企业的利润服务；必须按照相关法规办事，工作过程必须符合法律过程，要公开透明、要公正、要讲成本效益。如果不是这样、而是为了少部分公民的利益而不考虑手段和过程，那么，且不说难以掌握标准，就是有了标准，也可能被信息时代开放共享和法制社会的发展主流所冲走。<BR><BR>　　业内人士大都知道，无线通讯互联网是个新兴市场，然而，如果不包括企业网和全球互联网的融合等等，那么，无线通讯的局域网就是个很有限的市场，许多美国等西方国家的跨国公司早在1990年代中期就主动放弃该类业务了，其中不少硬软件加工生产已经转移到发展中国家；这样一来，他们可以把无线局域网的发展重心放在包括卫星通讯服务、企业网及其跟全球互联网融合等方面。例如，据美国主要报刊媒体报道，这一年，英特尔公司花费了大约3亿美元推广它的无线通讯新产品，该产品的主要特征就是跟全球互联网融合。此外，到今年一季度，仅硅谷的风险投资就达到40亿美元，其对象就是跟全球互联网融合的无线通讯网技术产品。就此发展动向看，WAPI一旦实行，就很可能把中国自己的相关企业推出全球互联网的新兴市场，对国际竞争者来说，那就等于少了一个很大的“潜在威胁”或很大的挑战者。既然如此，为什么美国等西方国家的政府和公司还那么重视WAPI的制定和推行、甚至态度很“硬”呢？<BR><BR>　　据笔者了解，那是“见微知著”；就WAPI来说，市场利益之争是表面现象，实质上，他们更关注的是中国在国际市场如何扮演自己的世界贸易组织成员的角色，是中国的信息技术及其市场的发展是走封闭捆绑的道路、还是走开放共享的道路。WAPI是针对国内市场的，却在西方国家引起广泛关注。这足以说明，中国的发展跟国际社会的联系越来越紧密，制定和推行国家标准等政府行为不能再继续过去的“我是中央帝国”的闭关锁国的思路，而要树立起国际视野和开放的国家自主发展的思路。中国政府决定无限期推迟WAPI标准的实施，是借助一个具体案例向本国、也向世界各国做了个旗帜鲜明的表态：今日中国已经不再是个闭关自守的“中央帝国”，中国将毫不含糊地履行它的发展中大国的责任，中国的发展崛起跟其它国家的关系不是“你死我活”或“我盈你亏”、而是“共赢”的和共同繁荣的关系。</P></DIV></FONT><STRONG><FONT face=楷体_GB2312><FONT color=#1919f8 size=5></FONT></FONT></STRONG><A></A>]]></description>
</item><item>
<title><![CDATA[脱线MIN算法]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=fengxin&amp;id=1962</link>
<author>fengxin</author>
<pubDate>2005/1/16 2:16:42</pubDate>
<description><![CDATA[
<P><FONT face=楷体_GB2312 color=#000000 size=5><STRONG>Union-Find算法的应用与推广</STRONG></FONT></P>
<P><FONT color=#000000>
<HR>
<BR><FONT face=Verdana size=3>指令Insert(i)：把元素i插入集合S中。<BR>指令Extract_min：从集合S中找出最小元并进行删除。<BR>两种指令的简单表示法：<BR>用i表示Insert(i)，用E表示Extract_min。<BR>例：7，2，5，9，E，6，E，E，3，E，1，4，E<BR>这种序列满足两个性质：<BR>1）任一i (1≤ i ≤n) 在序列中最多出现一次（元素之间互不相同）；<BR>2）从左起任意一段中，插入指令条数要大于等于E指令条数。<BR>（否则无元素可删。）<BR>脱线MIN问题：<BR>给定一个Insert与Extract_min的指令序列σ之后，<BR>对在序列中出现的每个i，<BR>算法要输出i是被第几条E指令删除的。<BR>（对于序列中未出现的i，算法应输出相应信息。）<BR>上例中有：1(5), 2(1), 3(4), 4(未被删除)，5(2)，6(3)，<BR>7，9与4一样未被删除，8未出现。<BR>因要对合并后的集合进行强制命名，<BR>故采用Aho书中的UNION(i,j,k)算法（可强制命名为k）：<BR>wlg assume COUNT[ROOT[i]] ≤ COUNT[ROOT[j]]<BR>(otherwise interchange i and j in the following lines)<BR>LARGE←ROOT[j];<BR>SMALL←ROOT[i];<BR>FATHER[SMALL]←LARGE;<BR>COUNT[LARGE]←COUNT[LARGE]+ COUNT[SMALL];<BR>NAME[LARGE]←k;<BR>ROOT[k]←LARGE;<BR>由于该算法不执行Find，故在O(1)时间里即可完成。<BR>算法开始之前，先把所有元素的所属集合名NAME[i]置为0（O(n)）；<BR>再扫描指令序列，把由E隔开的每段中的元素<BR>组成若干个集合并命名（O(n)）：<BR>e.g.: 1=，2=，3={}9,7,5,2{}6&#8709;，4={}3，5={}4,1，6= &#8709;<BR>用集合名（数字）来表示删除i的E指令序号，<BR>算法从i=1开始逐一检查，找到1所在的元素集合名（5），<BR>输出1是被第5条E指令删除的;<BR>输出后用UNION算法把集合5与6合并为6：6=。 {4,1 }<BR>下一步看i=2，找到2所在的元素集合名（1），<BR>输出2是被第1条E指令删除的;<BR>输出后用UNION算法把集合1、2合并, 得到2={2,5,6,7,9}。<BR>其次看i=3，找到3所在的元素集合名（4），<BR>输出3是被第4条E指令删除的；<BR>输出后用UNION算法把集合4与6合并(集合5已经不存在了)，<BR>得到6={1,3,4}。<BR>i=4时，找到4所在的元素集合名（6），<BR>但6&gt;E指令条数（只有5条），故输出“4未被删除”。<BR>i=5时，找到5所在的元素集合名（2），<BR>输出5是被第2条E指令删除的；<BR>输出后用UNION算法把集合2与3合并，得3={2,5,6,7,9}。<BR>i=6时，找到6所在的元素集合名（3），<BR>输出6是被第3条E指令删除的；<BR>输出后用UNION算法把集合3与6合并，得6={1,2,3,4,5,6,7,9}<BR>其后的7，9与4一样未被删除，<BR>而8未在序列中出现，因Find(8)=0，故应输出“8未出现”。<BR>为合并时方便地找到后继集合，引入Pred和Succ 2个数组：<BR>Pred[j]记录了前一个集合的名称(数字),初始时为j-1，<BR>Succ[j]记录了后一个集合的名称(数字),初始时为j+1。<BR>算法：<BR>for i=1 to n do {<BR>j←FIND(i);<BR>if j=0 then {输出“i未在序列中出现”}<BR>else if j&gt;k then {输出“i未被删除”}<BR>else{ /* i确实被删除了*/<BR>输出“i被第j条E指令所删除”；<BR>UNION(j，Succ[j]，Succ[j])；<BR>Succ[Pred[j]]←Succ[j]；/* 集合j不再存在*/<BR>Pred[Succ[j]]←Pred[j]<BR>}<BR>}<BR>算法的主要工作是执行O(n)条FIND指令，<BR>（其余工作在循环的每一轮都是常数时间）<BR>故该算法的时间复杂度为O(n*G(n))。</FONT></FONT><A href="http://w3china.org/blog/blog.asp?name=fengxin"></A>
<P></P>]]></description>
</item><item>
<title><![CDATA[KMP、Monte Carlo、Las Vegas 匹配算法 (用C++ & STL实现)]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=fengxin&amp;id=911</link>
<author>fengxin</author>
<pubDate>2004/12/11 19:59:02</pubDate>
<description><![CDATA[
<P>// StrMatcher.cpp : 定义控制台应用程序的入口点。</P>
<P>#include "stdafx.h"<BR>#include "basicmatcher.h"</P>
<P>#include &lt;stdlib.h&gt;<BR>#include &lt;stdio.h&gt;<BR>#include &lt;time.h&gt;</P>
<P>#define STRING_NUM 10000</P>
<P>//素数(Prime Number)产生器，这里利用讲义中提供的7素数分类，而非自行产生<BR>//PNM is short for "Prime Number Matrix"<BR>namespace PNM{<BR>&nbsp;//不同范围内的素数矩阵<BR>&nbsp;int pnm[6][7]={<BR>&nbsp;&nbsp;{211,223,227,229,233,239,241},<BR>&nbsp;&nbsp;{461,463,467,479,487,491,499},<BR>&nbsp;&nbsp;{953,967,971,977,983,991,997},<BR>&nbsp;&nbsp;{4957,4967,4969,4973,4987,4993,4999},<BR>&nbsp;&nbsp;{9923,9929,9931,9941,9949,9967,9973},<BR>&nbsp;&nbsp;{100003,100019,100043,100049,100057,100069,100103}<BR>&nbsp;};</P>
<P>&nbsp;//根据m、n的大小确定M，并随机抽取一个适当的素数<BR>&nbsp;//注意在全局调用前应使用srand((unsigned)time(NULL));<BR>&nbsp;int Generate(const CmpStrPair&amp; csp){<BR>&nbsp;&nbsp;int m=csp.second.second,n=csp.first.second;<BR>&nbsp;&nbsp;int M=2*m*n*n;&nbsp;&nbsp;//M=2*m*n*n<BR>&nbsp;&nbsp;int N=rand()%7;&nbsp;&nbsp;//N为随机数(0&lt;=N&lt;=6)<BR>&nbsp;&nbsp;if(M&gt;0 &amp;&amp; M&lt;=250) return pnm[0][N];<BR>&nbsp;&nbsp;else if(M&gt;250 &amp;&amp; M&lt;=500) return pnm[1][N];<BR>&nbsp;&nbsp;else if(M&gt;500 &amp;&amp; M&lt;=1000) return pnm[2][N];<BR>&nbsp;&nbsp;else if(M&gt;1000 &amp;&amp; M&lt;=5000) return pnm[3][N];<BR>&nbsp;&nbsp;else if(M&gt;5000 &amp;&amp; M&lt;=10000) return pnm[4][N];<BR>&nbsp;&nbsp;else return pnm[5][N];<BR>&nbsp;}<BR>};</P>
<P>//计时器变量<BR>clock_t start,step;<BR>//计时结果<BR>double total_time,run_time;</P>
<P>int _tmain(int argc, _TCHAR* argv[])<BR>{<BR>&nbsp;//产生大于5000组的随机串<BR>&nbsp;CmpStrVector csv;<BR>&nbsp;Romdomizer rmd;<BR>&nbsp;for(int i=0;i&lt;STRING_NUM;i++){<BR>&nbsp;&nbsp;rmd.Romdomize(csv);<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;//就上述产生的随机串进行各类匹配操作<BR>&nbsp;<BR>&nbsp;//KMP<BR>&nbsp;KMP kmp(csv[0]);<BR>&nbsp;start=clock();<BR>&nbsp;for(int i=0;i&lt;STRING_NUM;i++){<BR>&nbsp;&nbsp;kmp.ReSet(csv[i]);<BR>&nbsp;&nbsp;kmp.FastFind();<BR>&nbsp;}<BR>&nbsp;step=clock();<BR>&nbsp;total_time=double(step-start)/CLOCKS_PER_SEC;<BR>&nbsp;run_time=total_time/STRING_NUM;<BR>&nbsp;cout&lt;&lt;"KMP Approach :"&lt;&lt;endl;<BR>&nbsp;cout&lt;&lt;"Total Time : "&lt;&lt;total_time&lt;&lt;endl<BR>&nbsp;&nbsp;&lt;&lt;"Run Time : "&lt;&lt;run_time&lt;&lt;endl&lt;&lt;endl<BR>&nbsp;&nbsp;&lt;&lt;"--------------------------------"&lt;&lt;endl&lt;&lt;endl;</P>
<P>&nbsp;//Monte Carlo<BR>&nbsp;MonteCarlo mc(csv[0]);<BR>&nbsp;start=clock();<BR>&nbsp;for(int i=0;i&lt;STRING_NUM;i++){<BR>&nbsp;&nbsp;mc.ReSet(csv[i]);<BR>&nbsp;&nbsp;mc.Compare(PNM::Generate(csv[i]));<BR>&nbsp;}<BR>&nbsp;step=clock();<BR>&nbsp;total_time=double(step-start)/CLOCKS_PER_SEC;<BR>&nbsp;run_time=total_time/STRING_NUM;<BR>&nbsp;cout&lt;&lt;"Monte Carlo Approach :"&lt;&lt;endl;<BR>&nbsp;cout&lt;&lt;"Total Time : "&lt;&lt;total_time&lt;&lt;endl<BR>&nbsp;&nbsp;&lt;&lt;"Run Time : "&lt;&lt;run_time&lt;&lt;endl&lt;&lt;endl<BR>&nbsp;&nbsp;&lt;&lt;"--------------------------------"&lt;&lt;endl&lt;&lt;endl;<BR>&nbsp;<BR>&nbsp;//Las Vegas<BR>&nbsp;LasVegas lv(csv[0]);<BR>&nbsp;start=clock();<BR>&nbsp;for(int i=0;i&lt;STRING_NUM;i++){<BR>&nbsp;&nbsp;lv.ReSet(csv[i]);<BR>&nbsp;&nbsp;lv.Compare(PNM::Generate(csv[i]));<BR>&nbsp;}<BR>&nbsp;step=clock();<BR>&nbsp;total_time=double(step-start)/CLOCKS_PER_SEC;<BR>&nbsp;run_time=total_time/STRING_NUM;<BR>&nbsp;cout&lt;&lt;"Las Vegas Approach :"&lt;&lt;endl;<BR>&nbsp;cout&lt;&lt;"Total Time : "&lt;&lt;total_time&lt;&lt;endl<BR>&nbsp;&nbsp;&lt;&lt;"Run Time : "&lt;&lt;run_time&lt;&lt;endl&lt;&lt;endl<BR>&nbsp;&nbsp;&lt;&lt;"--------------------------------"&lt;&lt;endl&lt;&lt;endl;</P>
<P>&nbsp;cout&lt;&lt;"Monte Carlo Approach's Mistake-Rate Is :"<BR>&nbsp;&nbsp;&lt;&lt;1-float(lv.match_num)/float(mc.match_num)&lt;&lt;endl;<BR>&nbsp;<BR>&nbsp;return 0;<BR>}</P>
<P>//Utility.h : 辅助工具的定义</P>
<P>#include&lt;iostream&gt;<BR>#include&lt;string&gt;<BR>#include&lt;bitset&gt;<BR>#include&lt;limits&gt;<BR>#include&lt;vector&gt;<BR>#include&lt;utility&gt;</P>
<P>using namespace std;</P>
<P>//程序中用到的一些预定义和函数<BR>namespace def{<BR>&nbsp;//比特流原型，包含了比特流的数值信息<BR>&nbsp;typedef bitset&lt;numeric_limits&lt;unsigned long&gt;::digits&gt; Bits;<BR>&nbsp;//比特流模拟串：后项为串实际长度（前截断）<BR>&nbsp;typedef pair&lt;Bits,unsigned int&gt; BitString;<BR>&nbsp;//比较串对：前项为源串，后项为目标串<BR>&nbsp;typedef pair&lt;BitString,BitString&gt; CmpStrPair;<BR>&nbsp;//比较串对向量：用来存储已经生成的比较串对序列<BR>&nbsp;typedef vector&lt;CmpStrPair&gt; CmpStrVector;<BR>&nbsp;//打印比特串BitString<BR>&nbsp;ostream&amp; operator &lt;&lt; (ostream&amp; os,BitString&amp; b_str);<BR>};</P>
<P>using namespace def;</P>
<P>//随机数列产生器类<BR>class Romdomizer{<BR>public:<BR>&nbsp;Romdomizer();<BR>&nbsp;//将生成的比较串对送到待处理序列中<BR>&nbsp;void Print(ostream&amp; os=cout){<BR>&nbsp;&nbsp;os&lt;&lt;"Source : "&lt;&lt;source&lt;&lt;endl<BR>&nbsp;&nbsp;&nbsp; &lt;&lt;"Target : "&lt;&lt;target&lt;&lt;endl;<BR>&nbsp;}<BR>&nbsp;bool Romdomize(CmpStrVector&amp;);<BR>&nbsp;void Romdomize();&nbsp;&nbsp;//随机生成新比较串对<BR>private:<BR>&nbsp;void Create(long&amp; v,unsigned int&amp; s,bool isSource);&nbsp;//随机产生比特串的必要构成：v,比特串值、s：比特串长</P>
<P>&nbsp;BitString source;&nbsp;&nbsp;//比较源串X：x1,...,xn<BR>&nbsp;BitString target;&nbsp;&nbsp;//比较目标串Y：y1,...,ym<BR>};</P>
<P>//Utility.cpp : 辅助工具的实现</P>
<P>#include "stdafx.h"<BR>#include "Utility.h"</P>
<P>#include &lt;stdlib.h&gt;<BR>#include &lt;stdio.h&gt;<BR>#include &lt;time.h&gt;</P>
<P>//---------------------------------------------------------------------------------------------</P>
<P>namespace def{<BR>&nbsp;ostream&amp; operator &lt;&lt; (ostream&amp; os,BitString&amp; b_str){<BR>&nbsp;&nbsp;for(int i=b_str.second-1;i&gt;=0;i--)<BR>&nbsp;&nbsp;&nbsp;os&lt;&lt;b_str.first[i];<BR>&nbsp;&nbsp;return os;<BR>&nbsp;}<BR>};</P>
<P>//---------------------------------------------------------------------------------------------</P>
<P>Romdomizer::Romdomizer(){<BR>&nbsp;//根据时间设置随机<BR>&nbsp;srand((unsigned)time(NULL));<BR>}</P>
<P>void Romdomizer::Create(long&amp; v,unsigned int&amp; s,bool isSource){<BR>&nbsp;//串长度的最大值（目标串的最大长度应为源串的长度，根据isSource是否成真确定是否为源串）<BR>&nbsp;size_t size_max;<BR>&nbsp;long value_max=1;&nbsp;&nbsp;//根据随即产生的串长度决定串值可能取的最大值<BR>&nbsp;<BR>&nbsp;//计算串长度的最大值<BR>&nbsp;if(isSource){<BR>&nbsp;&nbsp;size_max=numeric_limits&lt;unsigned long&gt;::digits;<BR>&nbsp;}<BR>&nbsp;else<BR>&nbsp;&nbsp;size_max=static_cast&lt;size_t&gt;(3*s/4+1);//为了增加成功率，假设目标串较短，长度不到源串3/4<BR>&nbsp;//产生随机串长度<BR>&nbsp;s=rand()%size_max+1;<BR>&nbsp;//根据串长计算串值的最大值<BR>&nbsp;for(int i=static_cast&lt;int&gt;(s);i&gt;0;i--)<BR>&nbsp;&nbsp;value_max*=2;<BR>&nbsp;value_max--;<BR>&nbsp;//产生随机串值<BR>&nbsp;v=value_max*(float(rand())/float(RAND_MAX));<BR>}</P>
<P>void Romdomizer::Romdomize(){<BR>&nbsp;long t_value=0;&nbsp;&nbsp;&nbsp;//临时串值<BR>&nbsp;unsigned int t_size=0;&nbsp;//临时串长度<BR>&nbsp;//随机生成源串<BR>&nbsp;Create(t_value,t_size,true);<BR>&nbsp;source=make_pair(Bits(t_value),t_size);<BR>&nbsp;//随机生成目标串<BR>&nbsp;Create(t_value,t_size,false);<BR>&nbsp;target=make_pair(Bits(t_value),t_size);<BR>}</P>
<P>bool Romdomizer::Romdomize(CmpStrVector&amp; v){<BR>&nbsp;if(v.size()&lt;v.max_size()){<BR>&nbsp;&nbsp;Romdomize();<BR>&nbsp;&nbsp;CmpStrPair t_p(source,target);<BR>&nbsp;&nbsp;v.push_back(t_p);<BR>&nbsp;&nbsp;return true;<BR>&nbsp;}<BR>&nbsp;return false;<BR>}</P>
<P>// BasicMatcher.h : 基本匹配函数的定义</P>
<P>#include "Utility.h"</P>
<P>//说明：这里使用BitString特殊表现形式，以下方法采用反向匹配机制<BR>//即ym,...,y1与xn,...,x1匹配，如果成功，则原先的正向串也必然匹配<BR>//反之，则原先的正向串必然不匹配</P>
<P>class KMP{<BR>public:<BR>&nbsp;KMP(CmpStrPair&amp; csp):p_source(&amp;csp.first),p_target(&amp;csp.second){<BR>&nbsp;&nbsp;for(int i=0;i&lt;numeric_limits&lt;unsigned long&gt;::digits;i++)<BR>&nbsp;&nbsp;&nbsp;fail[i]=-2;<BR>&nbsp;&nbsp;Fail();<BR>&nbsp;}<BR>&nbsp;inline void ReSet(CmpStrPair&amp; csp){<BR>&nbsp;&nbsp;p_source=&amp;csp.first;<BR>&nbsp;&nbsp;p_target=&amp;csp.second;<BR>&nbsp;&nbsp;for(int i=0;i&lt;numeric_limits&lt;unsigned long&gt;::digits;i++)<BR>&nbsp;&nbsp;&nbsp;fail[i]=-2;<BR>&nbsp;&nbsp;Fail();<BR>&nbsp;}<BR>&nbsp;int FastFind();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//KMP的主匹配函数<BR>private:<BR>&nbsp;void Fail();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//失效函数<BR>&nbsp;BitString* p_source;&nbsp;&nbsp;&nbsp;//源串的引用<BR>&nbsp;BitString* p_target;&nbsp;&nbsp;&nbsp;//目标串的引用<BR>&nbsp;int fail[numeric_limits&lt;unsigned long&gt;::digits];<BR>};</P>
<P>class MonteCarlo{<BR>public:<BR>&nbsp;MonteCarlo(CmpStrPair&amp; csp):p_source(&amp;csp.first),p_target(&amp;csp.second),match_num(0){;}<BR>&nbsp;inline void ReSet(CmpStrPair&amp; csp){<BR>&nbsp;&nbsp;p_source=&amp;csp.first;<BR>&nbsp;&nbsp;p_target=&amp;csp.second;<BR>&nbsp;}</P>
<P>&nbsp;//Monte Carlo的匹配主函数<BR>&nbsp;//p:随机产生的素数<BR>&nbsp;int Compare(int p);<BR>&nbsp;int match_num;<BR>private:<BR>&nbsp;BitString* p_source;&nbsp;&nbsp;&nbsp;//源串的引用<BR>&nbsp;BitString* p_target;&nbsp;&nbsp;&nbsp;//目标串的引用<BR>};</P>
<P>class LasVegas{<BR>public:<BR>&nbsp;LasVegas(CmpStrPair&amp; csp):p_source(&amp;csp.first),p_target(&amp;csp.second),match_num(0){;}<BR>&nbsp;inline void ReSet(CmpStrPair&amp; csp){<BR>&nbsp;&nbsp;p_source=&amp;csp.first;<BR>&nbsp;&nbsp;p_target=&amp;csp.second;<BR>&nbsp;}</P>
<P>&nbsp;//Last Vegas的匹配主函数<BR>&nbsp;//p:随机产生的素数<BR>&nbsp;bool Verify(int pos);<BR>&nbsp;int Compare(int p);<BR>&nbsp;int match_num;<BR>private:<BR>&nbsp;BitString* p_source;&nbsp;&nbsp;&nbsp;//源串的引用<BR>&nbsp;BitString* p_target;&nbsp;&nbsp;&nbsp;//目标串的引用<BR>};</P>
<P>//BasicMatcher.cpp : 基本匹配函数的实现</P>
<P>#include "stdafx.h"<BR>#include "BasicMatcher.h"</P>
<P>void KMP::Fail(){<BR>&nbsp;int length=p_target-&gt;second;<BR>&nbsp;fail[0]=-1;<BR>&nbsp;for(int j=1;j&lt;length;j++){<BR>&nbsp;&nbsp;int i=fail[j-1];<BR>&nbsp;&nbsp;while(p_target-&gt;first[j]!=p_target-&gt;first[i+1] &amp;&amp; i&gt;=0) i=fail[i];<BR>&nbsp;&nbsp;if(p_target-&gt;first[j]==p_target-&gt;first[i+1]) fail[j]=i+1;<BR>&nbsp;&nbsp;else fail[j]=-1;<BR>&nbsp;}<BR>}</P>
<P>int KMP::FastFind(){<BR>&nbsp;int posS=0,posT=0;<BR>&nbsp;int lenS=p_source-&gt;second,lenT=p_target-&gt;second;<BR>&nbsp;while(posT&lt;lenT &amp;&amp; posS&lt;lenS)<BR>&nbsp;&nbsp;if(p_target-&gt;first[posT]==p_source-&gt;first[posS]){<BR>&nbsp;&nbsp;&nbsp;posT++;<BR>&nbsp;&nbsp;&nbsp;posS++;<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;else if(posT==0) posS++;<BR>&nbsp;&nbsp;else posT=fail[posT-1]+1;<BR>&nbsp;if(posT&lt;lenT)<BR>&nbsp;&nbsp;return 0;<BR>&nbsp;else return posS-lenT+1;//方向匹配结果<BR>}</P>
<P>int MonteCarlo::Compare(int p){<BR>&nbsp;int n=p_source-&gt;second,m=p_target-&gt;second;<BR>&nbsp;int j=0;<BR>&nbsp;int temp=1;&nbsp;&nbsp;//用以求得2的m次方<BR>&nbsp;for(int i=0;i&lt;m;i++)<BR>&nbsp;&nbsp;temp*=2;<BR>&nbsp;int Wp=temp % p;<BR>&nbsp;//求源串指纹<BR>&nbsp;//先求以X0开头m位的数值value作为递推的起点;<BR>&nbsp;long value=0;<BR>&nbsp;for(int i=0;i&lt;m;i++){<BR>&nbsp;&nbsp;value*=2;<BR>&nbsp;&nbsp;int bit=p_source-&gt;first[i]?1:0;<BR>&nbsp;&nbsp;value+=bit;<BR>&nbsp;}<BR>&nbsp;int Ipx=value % p;<BR>&nbsp;int Ipy=p_target-&gt;first.to_ulong() % p;&nbsp;//求目标串指纹<BR>&nbsp;while(j&lt;n-m){<BR>&nbsp;&nbsp;if(Ipx==Ipy){<BR>&nbsp;&nbsp;&nbsp;match_num++;<BR>&nbsp;&nbsp;&nbsp;return j+1;<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;//根据公式计算Ipx<BR>&nbsp;&nbsp;int t1=p_source-&gt;first[j]?1:0;<BR>&nbsp;&nbsp;int t2=p_source-&gt;first[j+m]?1:0;<BR>&nbsp;&nbsp;Ipx=(2*Ipx+t1*Wp+t2) % p;<BR>&nbsp;&nbsp;j++;<BR>&nbsp;}<BR>&nbsp;return 0;<BR>}</P>
<P>bool LasVegas::Verify(int pos){<BR>&nbsp;for(int i=0;i&lt;p_target-&gt;second;i++){<BR>&nbsp;&nbsp;if(p_target-&gt;first[i]!=p_source-&gt;first[pos+i])<BR>&nbsp;&nbsp;&nbsp;return false;<BR>&nbsp;}<BR>&nbsp;return true;<BR>}</P>
<P>int LasVegas::Compare(int p){<BR>&nbsp;int n=p_source-&gt;second,m=p_target-&gt;second;<BR>&nbsp;int j=0;<BR>&nbsp;int temp=1;&nbsp;&nbsp;//用以求得2的m次方<BR>&nbsp;for(int i=0;i&lt;m;i++)<BR>&nbsp;&nbsp;temp*=2;<BR>&nbsp;int Wp=temp % p;<BR>&nbsp;long value=0;<BR>&nbsp;for(int i=0;i&lt;m;i++){<BR>&nbsp;&nbsp;value*=2;<BR>&nbsp;&nbsp;int bit=p_source-&gt;first[i]?1:0;<BR>&nbsp;&nbsp;value+=bit;<BR>&nbsp;}<BR>&nbsp;int Ipx=value % p;<BR>&nbsp;int Ipy=p_target-&gt;first.to_ulong() % p;&nbsp;//求目标串指纹<BR>&nbsp;while(j&lt;n-m){<BR>&nbsp;&nbsp;//加入了验证环节<BR>&nbsp;&nbsp;if(Ipx==Ipy &amp;&amp; Verify(j)) {<BR>&nbsp;&nbsp;&nbsp;match_num++;<BR>&nbsp;&nbsp;&nbsp;return j+1;<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;//根据公式计算Ipx<BR>&nbsp;&nbsp;int t1=p_source-&gt;first[j]?1:0;<BR>&nbsp;&nbsp;int t2=p_source-&gt;first[j+m]?1:0;<BR>&nbsp;&nbsp;Ipx=(2*Ipx+t1*Wp+t2) % p;<BR>&nbsp;&nbsp;j++;<BR>&nbsp;}<BR>&nbsp;return 0;<BR>}</P>]]></description>
</item><item>
<title><![CDATA[数据库设计经验谈]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=fengxin&amp;id=910</link>
<author>fengxin</author>
<pubDate>2004/12/11 18:20:52</pubDate>
<description><![CDATA[<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数据库设计经验谈(夜来香)<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个成功的管理系统，是由：[50%的业务 + 50%的软件]所组成，<BR>&nbsp;&nbsp;&nbsp; 而 50%的成功软件又有[25%的数据库 + 25%的程序]所组成，数据库<BR>&nbsp;&nbsp;&nbsp; 设计的好坏是一个关键。如果把企业的数据比做生命所必需的血液，<BR>&nbsp;&nbsp;&nbsp; 那么数据库的设计就是应用中最重要的一部分。有关数据库设计的材<BR>&nbsp;&nbsp;&nbsp; 料汗牛充栋，大学学位课程里也有专门的讲述。不过，就如我们反复<BR>&nbsp;&nbsp;&nbsp; 强调的那样，再好的老师也比不过经验的教诲。所以我归纳历年来所<BR>&nbsp;&nbsp;&nbsp; 走的弯路及体会，并在网上找了些对数据库设计颇有造诣的专业人士<BR>&nbsp;&nbsp;&nbsp; 给大家传授一些设计数据库的技巧和经验。精选了其中的 60 个最佳<BR>&nbsp;&nbsp;&nbsp; 技巧，并把这些技巧编写成了本文，为了方便索引其内容划分为 5 <BR>&nbsp;&nbsp;&nbsp; 个部分：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第 1 部分 - 设计数据库之前<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这一部分罗列了 12 个基本技巧，包括命名规范和明确业务需求<BR>&nbsp;&nbsp;&nbsp; 等。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第 2 部分 - 设计数据库表<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 总共 24 个指南性技巧，涵盖表内字段设计以及应该避免的常见<BR>&nbsp;&nbsp;&nbsp; 问题等。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第 3 部分 - 选择键<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 怎么选择键呢？这里有 10 个技巧专门涉及系统生成的主键的正<BR>&nbsp;&nbsp;&nbsp; 确用法，还有何时以及如何索引字段以获得最佳性能等。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第 4 部分 - 保证数据完整性<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 讨论如何保持数据库的清晰和健壮，如何把有害数据降低到最小<BR>&nbsp;&nbsp;&nbsp; 程度。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第 5 部分 - 各种小技巧<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不包括在以上 4 个部分中的其他技巧，五花八门，有了它们希<BR>&nbsp;&nbsp;&nbsp; 望你的数据库开发工作会更轻松恍?<BR><BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; § 第 1 部分 - 设计数据库之前<BR>&nbsp;&nbsp;&nbsp; ──────────────<BR><BR>&nbsp;&nbsp;&nbsp; ■ 考察现有环境<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在设计一个新数据库时，你不但应该仔细研究业务需求而且还要<BR>&nbsp;&nbsp;&nbsp; 考察现有的系统。大多数数据库项目都不是从头开始建立的；通常，<BR>&nbsp;&nbsp;&nbsp; 机构内总会存在用来满足特定需求的现有系统（可能没有实现自动计<BR>&nbsp;&nbsp;&nbsp; 算）。显然，现有系统并不完美，否则你就不必再建立新系统了。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但是对旧系统的研究可以让你发现一些可能会忽略的细微问题。<BR>&nbsp;&nbsp;&nbsp; 一般来说，考察现有系统对你绝对有好处。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 定义标准的对象命名规范<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一定要定义数据库对象的命名规范。对数据库表来说，从项目一<BR>&nbsp;&nbsp;&nbsp; 开始就要确定表名是采用复数还是单数形式。此外还要给表的别名定<BR>&nbsp;&nbsp;&nbsp; 义简单规则（比方说，如果表名是一个单词，别名就取单词的前 4 <BR>&nbsp;&nbsp;&nbsp; 个字母；如果表名是两个单词，就各取两个单词的前两个字母组成 <BR>&nbsp;&nbsp;&nbsp; 4 个字母长的别名；如果表的名字由 3 个单词组成，你不妨从头两<BR>&nbsp;&nbsp;&nbsp; 个单词中各取一个然后从最后一个单词中再取出两个字母，结果还是<BR>&nbsp;&nbsp;&nbsp; 组成 4 字母长的别名，其余依次类推）对工作用表来说，表名可以<BR>&nbsp;&nbsp;&nbsp; 加上前缀 WORK_ 后面附上采用该表的应用程序的名字。表内的列[ <BR>&nbsp;&nbsp;&nbsp; 字段]要针对键采用一整套设计规则。比如，如果键是数字类型，你<BR>&nbsp;&nbsp;&nbsp; 可以用 _N 作为后缀；<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果是字符类型则可以采用 _C 后缀。对列[字段]名应该采用标<BR>&nbsp;&nbsp;&nbsp; 准的前缀和后缀。再如，假如你的表里有好多“money”字段，你不<BR>&nbsp;&nbsp;&nbsp; 妨给每个列[字段]增加一个 _M 后缀。还有，日期列[字段]最好以 <BR>&nbsp;&nbsp;&nbsp; D_ 作为名字打头。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 检查表名、报表名和查询名之间的命名规范。你可能会很快就被<BR>&nbsp;&nbsp;&nbsp; 这些不同的数据库要素的名称搞糊涂了。假如你坚持统一地命名这些<BR>&nbsp;&nbsp;&nbsp; 数据库的不同组成部分，至少你应该在这些对象名字的开头用 <BR>&nbsp;&nbsp;&nbsp; Table、Query 或者 Report 等前缀加以区别。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果采用了 Microsoft Access，你可以用 qry、rpt、tbl 和 <BR>&nbsp;&nbsp;&nbsp; mod 等符号来标识对象（比如 tbl_Employees）。我在和 SQL <BR>&nbsp;&nbsp;&nbsp; Server 打交道的时候还用过 tbl 来索引表，但我用 sp_company <BR>&nbsp;&nbsp;&nbsp; （现在用 sp_feft_）标识存储过程，因为在有的时候如果我发现了<BR>&nbsp;&nbsp;&nbsp; 更好的处理办法往往会保存好几个拷贝。我在实现 SQL Server <BR>&nbsp;&nbsp;&nbsp; 2000 时用 udf_ （或者类似的标记）标识我编写的函数。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 工欲善其事, 必先利其器采用理想的数据库设计工具，比如：<BR>&nbsp;&nbsp;&nbsp; SyBase 公司的 PowerDesign，她支持 PB、VB、Delp he 等语言，通<BR>&nbsp;&nbsp;&nbsp; 过 ODBC 可以连接市面上流行的 30 多个数据库，包括 dBase、<BR>&nbsp;&nbsp;&nbsp; FoxPro、V FP、SQL Server 等，今后有机会我将着重介绍 <BR>&nbsp;&nbsp;&nbsp; PowerDesign 的使用。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 获取数据模式资源手册<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 正在寻求示例模式的人可以阅读《数据模式资源手册》一书，该<BR>&nbsp;&nbsp;&nbsp; 书由 Len Silverston、W . H. Inmon 和 Kent Graziano 编写，是<BR>&nbsp;&nbsp;&nbsp; 一本值得拥有的最佳数据建模图书。该书包括的章节涵盖多种数据领<BR>&nbsp;&nbsp;&nbsp; 域，比如人员、机构和工作效能等。其他的你还可以参考：[1]萨师<BR>&nbsp;&nbsp;&nbsp; 煊王珊著数据库系统概论(第二版)高等教育出版社 1991、[2][美] <BR>&nbsp;&nbsp;&nbsp; Steven M.Bobrowsk i 著 Oracle 7 与客户／服务器计算技术从入门<BR>&nbsp;&nbsp;&nbsp; 到精通刘建元等译电子工业出版社， 1996、[3]周中元信息系统建模<BR>&nbsp;&nbsp;&nbsp; 方法(下)　电子与信息化　1999年第3期，1999 畅想未来，但不可忘<BR>&nbsp;&nbsp;&nbsp; 了过去的教训我发现询问用户如何看待未来需求变化非常有用。这样<BR>&nbsp;&nbsp;&nbsp; 做可以达到两个目的：首先，你可以清楚地了解应用设计在哪个地方<BR>&nbsp;&nbsp;&nbsp; 应该更具灵活性以及如何避免性能瓶颈；其次，你知道发生事先没有<BR>&nbsp;&nbsp;&nbsp; 确定的需求变更时用户将和你一样感到吃惊。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一定要记住过去的经验教训！我们开发人员还应该通过分享自己<BR>&nbsp;&nbsp;&nbsp; 的体会和经验互相帮助。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 即使用户认为他们再也不需要什么支持了，我们也应该对他们进<BR>&nbsp;&nbsp;&nbsp; 行这方面的教育，我们都曾经面临过这样的时刻“当初要是这么做了<BR>&nbsp;&nbsp;&nbsp; 该多好..”。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 在物理实践之前进行逻辑设计<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在深入物理设计之前要先进行逻辑设计。随着大量的 CASE 工具<BR>&nbsp;&nbsp;&nbsp; 不断涌现出来，你的设计也可以达到相当高的逻辑水准，你通常可以<BR>&nbsp;&nbsp;&nbsp; 从整体上更好地了解数据库设计所需要的方方面面。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 了解你的业务<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在你百分百地确定系统从客户角度满足其需求之前不要在你的 <BR>&nbsp;&nbsp;&nbsp; ER（实体关系）模式中加入哪怕一个数据表（怎么，你还没有模式？<BR>&nbsp;&nbsp;&nbsp; 那请你参看技巧 9）。了解你的企业业务可以在以后的开发阶段节约<BR>&nbsp;&nbsp;&nbsp; 大量的时间。一旦你明确了业务需求，你就可以自己做出许多决策了。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一旦你认为你已经明确了业务内容，你最好同客户进行一次系统<BR>&nbsp;&nbsp;&nbsp; 的交流。采用客户的术语并且向他们解释你所想到的和你所听到的。<BR>&nbsp;&nbsp;&nbsp; 同时还应该用可能、将会和必须等词汇表达出系统的关系基数。这样<BR>&nbsp;&nbsp;&nbsp; 你就可以让你的客户纠正你自己的理解然后做好下一步的 ER 设计。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 创建数据字典和 ER 图表<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一定要花点时间创建 ER 图表和数据字典。其中至少应该包含每<BR>&nbsp;&nbsp;&nbsp; 个字段的数据类型和在每个表内的主外键。创建 ER 图表和数据字典<BR>&nbsp;&nbsp;&nbsp; 确实有点费时但对其他开发人员要了解整个设计却是完全必要的。越<BR>&nbsp;&nbsp;&nbsp; 早创建越能有助于避免今后面临的可能混乱，从而可以让任何了解数<BR>&nbsp;&nbsp;&nbsp; 据库的人都明确如何从数据库中获得数据。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有一份诸如 ER 图表等最新文档其重要性如何强调都不过分，这<BR>&nbsp;&nbsp;&nbsp; 对表明表之间关系很有用，而数据字典则说明了每个字段的用途以及<BR>&nbsp;&nbsp;&nbsp; 任何可能存在的别名。对 SQL 表达式的文档化来说这是完全必要的。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 创建模式<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一张图表胜过千言万语：开发人员不仅要阅读和实现它，而且还<BR>&nbsp;&nbsp;&nbsp; 要用它来帮助自己和用户对话。模式有助于提高协作效能，这样在先<BR>&nbsp;&nbsp;&nbsp; 期的数据库设计中几乎不可能出现大的问题。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 模式不必弄的很复杂；甚至可以简单到手写在一张纸上就可以了。<BR>&nbsp;&nbsp;&nbsp; 只是要保证其上的逻辑关系今后能产生效益。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 从输入输出下手<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在定义数据库表和字段需求（输入）时，首先应检查现有的或者<BR>&nbsp;&nbsp;&nbsp; 已经设计出的报表、查询和视图（输出）以决定为了支持这些输出哪<BR>&nbsp;&nbsp;&nbsp; 些是必要的表和字段。举个简单的例子：假如客户需要一个报表按照<BR>&nbsp;&nbsp;&nbsp; 邮政编码排序、分段和求和，你要保证其中包括了单独的邮政编码字<BR>&nbsp;&nbsp;&nbsp; 段而不要把邮政编码糅进地址字段里。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 报表技巧<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要了解用户通常是如何报告数据的：批处理还是在线提交报表？<BR>&nbsp;&nbsp;&nbsp; 时间间隔是每天、每周、每月、每个季度还是每年？如果需要的话还<BR>&nbsp;&nbsp;&nbsp; 可以考虑创建总结表。系统生成的主键在报表中很难管理。用户在具<BR>&nbsp;&nbsp;&nbsp; 有系统生成主键的表内用副键进行检索往往会返回许多重复数据。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样的检索性能比较低而且容易引起混乱。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 理解客户需求<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看起来这应该是显而易见的事，但需求就是来自客户（这里要从<BR>&nbsp;&nbsp;&nbsp; 内部和外部客户的角度考虑）。不要依赖用户写下来的需求，真正的<BR>&nbsp;&nbsp;&nbsp; 需求在客户的脑袋里。你要让客户解释其需求，而且随着开发的继续，<BR>&nbsp;&nbsp;&nbsp; 还要经常询问客户保证其需求仍然在开发的目的之中。一个不变的真<BR>&nbsp;&nbsp;&nbsp; 理是：“只有我看见了我才知道我想要的是什么”必然会导致大量的<BR>&nbsp;&nbsp;&nbsp; 返工，因为数据库没有达到客户从来没有写下来的需求标准。而更糟<BR>&nbsp;&nbsp;&nbsp; 的是你对他们需求的解释只属于你自己，而且可能是完全错误的。<BR><BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; § 第 2 部分 - 设计表和字段<BR>&nbsp;&nbsp;&nbsp; ──────────────<BR><BR>&nbsp;&nbsp;&nbsp; ■ 检查各种变化<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我在设计数据库的时候会考虑到哪些数据字段将来可能会发生变<BR>&nbsp;&nbsp;&nbsp; 更。比方说，姓氏就是如此（注意是西方人的姓氏，比如女性结婚后<BR>&nbsp;&nbsp;&nbsp; 从夫姓等）。所以，在建立系统存储客户信息时，我倾向于在单独的<BR>&nbsp;&nbsp;&nbsp; 一个数据表里存储姓氏字段，而且还附加起始日和终止日等字段，这<BR>&nbsp;&nbsp;&nbsp; 样就可以跟踪这一数据条目的变化。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 采用有意义的字段名<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有一回我参加开发过一个项目，其中有从其他程序员那里继承的<BR>&nbsp;&nbsp;&nbsp; 程序，那个程序员喜欢用屏幕上显示数据指示用语命名字段，这也不<BR>&nbsp;&nbsp;&nbsp; 赖，但不幸的是，她还喜欢用一些奇怪的命名法，其命名采用了匈牙<BR>&nbsp;&nbsp;&nbsp; 利命名和控制序号的组合形式，比如 cbo1、txt2、txt2_b 等等。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 除非你在使用只面向你的缩写字段名的系统，否则请尽可能地把<BR>&nbsp;&nbsp;&nbsp; 字段描述的清楚些。当然，也别做过头了，比如 <BR>&nbsp;&nbsp;&nbsp; Customer_Shipping_Address_Street_Line_1，虽然很富有说明性，<BR>&nbsp;&nbsp;&nbsp; 但没人愿意键入这么长的名字，具体尺度就在你的把握中。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 采用前缀命名<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果多个表里有好多同一类型的字段（比如 FirstName），你不<BR>&nbsp;&nbsp;&nbsp; 妨用特定表的前缀（比如 CusLastName）来帮助你标识字段。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 时效性数据应包括“最近更新日期/时间”字段。时间标记对查<BR>&nbsp;&nbsp;&nbsp; 找数据问题的原因、按日期重新处理/重载数据和清除旧数据特别有<BR>&nbsp;&nbsp;&nbsp; 用。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 标准化和数据驱动<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数据的标准化不仅方便了自己而且也方便了其他人。比方说，假<BR>&nbsp;&nbsp;&nbsp; 如你的用户界面要访问外部数据源（文件、XML 文档、其他数据库等），<BR>&nbsp;&nbsp;&nbsp; 你不妨把相应的连接和路径信息存储在用户界面支持表里。还有，如<BR>&nbsp;&nbsp;&nbsp; 果用户界面执行工作流之类的任务（发送邮件、打印信笺、修改记录<BR>&nbsp;&nbsp;&nbsp; 状态等），那么产生工作流的数据也可以存放在数据库里。预先安排<BR>&nbsp;&nbsp;&nbsp; 总需要付出努力，但如果这些过程采用数据驱动而非硬编码的方式，<BR>&nbsp;&nbsp;&nbsp; 那么策略变更和维护都会方便得多。事实上，如果过程是数据驱动的，<BR>&nbsp;&nbsp;&nbsp; 你就可以把相当大的责任推给用户，由用户来维护自己的工作流过程。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 标准化不能过头<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对那些不熟悉标准化一词（normalization）的人而言，标准化<BR>&nbsp;&nbsp;&nbsp; 可以保证表内的字段都是最基础的要素，而这一措施有助于消除数据<BR>&nbsp;&nbsp;&nbsp; 库中的数据冗余。标准化有好几种形式，但 Thi rd Normal Form<BR>&nbsp;&nbsp;&nbsp; （3NF）通常被认为在性能、扩展性和数据完整性方面达到了最好平<BR>&nbsp;&nbsp;&nbsp; 衡。简单来说，3NF 规定：<BR><BR>&nbsp;&nbsp;&nbsp; ·　表内的每一个值都只能被表达一次。<BR>&nbsp;&nbsp;&nbsp; ·　表内的每一行都应该被唯一的标识（有唯一键）。<BR>&nbsp;&nbsp;&nbsp; ·　表内不应该存储依赖于其他键的非键信息。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 遵守 3NF 标准的数据库具有以下特点：有一组表专门存放通过<BR>&nbsp;&nbsp;&nbsp; 键连接起来的关联数据。比方说，某个存放客户及其有关定单的 <BR>&nbsp;&nbsp;&nbsp; 3NF 数据库就可能有两个表：Customer 和 Order。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Order 表不包含定单关联客户的任何信息，但表内会存放一个键<BR>&nbsp;&nbsp;&nbsp; 值，该键指向 Customer 表里包含该客户信息的那一行。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 更高层次的标准化也有，但更标准是否就一定更好呢？答案是不<BR>&nbsp;&nbsp;&nbsp; 一定。事实上，对某些项目来说，甚至就连 3NF 都可能给数据库引<BR>&nbsp;&nbsp;&nbsp; 入太高的复杂性。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了效率的缘故，对表不进行标准化有时也是必要的，这样的例<BR>&nbsp;&nbsp;&nbsp; 子很多。曾经有个开发餐饮分析软件的活就是用非标准化表把查询时<BR>&nbsp;&nbsp;&nbsp; 间从平均 40 秒降低到了两秒左右。虽然我不得不这么做，但我绝不<BR>&nbsp;&nbsp;&nbsp; 把数据表的非标准化当作当然的设计理念。而具体的操作不过是一种<BR>&nbsp;&nbsp;&nbsp; 派生。所以如果表出了问题重新产生非标准化的表是完全可能的。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Microsoft Visual FoxPro 报表技巧如果你正在使用 <BR>&nbsp;&nbsp;&nbsp; Microsoft Visual FoxPro，你可以用对用户友好的字段名来代替编<BR>&nbsp;&nbsp;&nbsp; 号的名称：比如用 Customer Name 代替 txtCNaM。这样，当你用向<BR>&nbsp;&nbsp;&nbsp; 导程序[Wizards，台湾人称为‘精灵’]创建表单和报表时，其名字<BR>&nbsp;&nbsp;&nbsp; 会让那些不是程序员的人更容易阅读。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 不活跃或者不采用的指示符<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 增加一个字段表示所在记录是否在业务中不再活跃挺有用的。不<BR>&nbsp;&nbsp;&nbsp; 管是客户、员工还是其他什么人，这样做都能有助于再运行查询的时<BR>&nbsp;&nbsp;&nbsp; 候过滤活跃或者不活跃状态。同时还消除了新用户在采用数据时所面<BR>&nbsp;&nbsp;&nbsp; 临的一些问题，比如，某些记录可能不再为他们所用，再删除的时候<BR>&nbsp;&nbsp;&nbsp; 可以起到一定的防范作用。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用角色实体定义属于某类别的列[字段]在需要对属于特定类别<BR>&nbsp;&nbsp;&nbsp; 或者具有特定角色的事物做定义时，可以用角色实体来创建特定的时<BR>&nbsp;&nbsp;&nbsp; 间关联关系，从而可以实现自我文档化。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里的含义不是让 PERSON 实体带有 Title 字段，而是说，为<BR>&nbsp;&nbsp;&nbsp; 什么不用 PERSON 实体和 PERSON_TYPE 实体来描述人员呢？比方说，<BR>&nbsp;&nbsp;&nbsp; 当 John Smith, Engineer 提升为 John Smit h, Director 乃至最<BR>&nbsp;&nbsp;&nbsp; 后爬到 John Smith, CIO 的高位，而所有你要做的不过是改变两个<BR>&nbsp;&nbsp;&nbsp; 表 PERSON 和 PERSON_TYPE 之间关系的键值，同时增加一个日期/时<BR>&nbsp;&nbsp;&nbsp; 间字段来知道变化是何时发生的。这样，你的 PERSON_TYPE 表就包<BR>&nbsp;&nbsp;&nbsp; 含了所有 PERSON 的可能类型，比如 Associ ate、Engineer、<BR>&nbsp;&nbsp;&nbsp; Director、CIO 或者 CEO 等。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 还有个替代办法就是改变 PERSON 记录来反映新头衔的变化，不<BR>&nbsp;&nbsp;&nbsp; 过这样一来在时间上无法跟踪个人所处位置的具体时间。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 采用常用实体命名机构数据<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 组织数据的最简单办法就是采用常用名字，比如：PERSON、<BR>&nbsp;&nbsp;&nbsp; ORGANIZATION、ADDRESS 和 P HONE 等等。当你把这些常用的一般名<BR>&nbsp;&nbsp;&nbsp; 字组合起来或者创建特定的相应副实体时，你就得到了自己用的特殊<BR>&nbsp;&nbsp;&nbsp; 版本。开始的时候采用一般术语的主要原因在于所有的具体用户都能<BR>&nbsp;&nbsp;&nbsp; 对抽象事物具体化。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有了这些抽象表示，你就可以在第 2 级标识中采用自己的特殊<BR>&nbsp;&nbsp;&nbsp; 名称，比如，PERSON 可能是 Employee、Spouse、Patient、<BR>&nbsp;&nbsp;&nbsp; Client、Customer、Vendor 或者 Teacher 等。同样的，<BR>&nbsp;&nbsp;&nbsp; ORGANIZATION 也可能是 MyCompany、MyDepartment、Competitor、<BR>&nbsp;&nbsp;&nbsp; Hospital、Warehouse、Government 等。最后 ADDRESS 可以具体为 <BR>&nbsp;&nbsp;&nbsp; Site、Location、Home、Work、Client、 Vendor、Corporate 和 <BR>&nbsp;&nbsp;&nbsp; FieldOffice 等。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 采用一般抽象术语来标识“事物”的类别可以让你在关联数据以<BR>&nbsp;&nbsp;&nbsp; 满足业务要求方面获得巨大的灵活性，同时这样做还可以显著降低数<BR>&nbsp;&nbsp;&nbsp; 据存储所需的冗余量。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 用户来自世界各地<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在设计用到网络或者具有其他国际特性的数据库时，一定要记住<BR>&nbsp;&nbsp;&nbsp; 大多数国家都有不同的字段格式，比如邮政编码等，有些国家，比如<BR>&nbsp;&nbsp;&nbsp; 新西兰就没有邮政编码一说。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 数据重复需要采用分立的数据表<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你发现自己在重复输入数据，请创建新表和新的关系。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个表中都应该添加的 3 个有用的字段 * <BR>&nbsp;&nbsp;&nbsp; dRecordCreationDate，在 VB 下默认是 Now()，而在 SQL Server <BR>&nbsp;&nbsp;&nbsp; 下默认为 GETDATE() * sRecordCreator，在 SQL Server 下默认为 <BR>&nbsp;&nbsp;&nbsp; NOT NULL DEFAULT USER * nRecordVersion，记录的版本标记；有助<BR>&nbsp;&nbsp;&nbsp; 于准确说明记录中出现 null 数据或者丢失数据的原因对地址和电话<BR>&nbsp;&nbsp;&nbsp; 采用多个字段描述街道地址就短短一行记录是不够的。<BR>&nbsp;&nbsp;&nbsp; Address_Line1、Address_Line2 和 Address_Li ne3 可以提供更大<BR>&nbsp;&nbsp;&nbsp; 的灵活性。还有，电话号码和邮件地址最好拥有自己的数据表，其间<BR>&nbsp;&nbsp;&nbsp; 具有自身的类型和标记类别。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 过分标准化可要小心，这样做可能会导致性能上出现问题。虽然<BR>&nbsp;&nbsp;&nbsp; 地址和电话表分离通常可以达到最佳状态，但是如果需要经常访问这<BR>&nbsp;&nbsp;&nbsp; 类信息，或许在其父表中存放“首选”信息（比如 Customer 等）更<BR>&nbsp;&nbsp;&nbsp; 为妥当些。非标准化和加速访问之间的妥协是有一定意义的。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 使用多个名称字段<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我觉得很吃惊，许多人在数据库里就给 name 留一个字段。我觉<BR>&nbsp;&nbsp;&nbsp; 得只有刚入门的开发人员才会这么做，但实际上网上这种做法非常普<BR>&nbsp;&nbsp;&nbsp; 遍。我建议应该把姓氏和名字当作两个字段来处理，然后在查询的时<BR>&nbsp;&nbsp;&nbsp; 候再把他们组合起来。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我最常用的是在同一表中创建一个计算列[字段]，通过它可以自<BR>&nbsp;&nbsp;&nbsp; 动地连接标准化后的字段，这样数据变动的时候它也跟着变。不过，<BR>&nbsp;&nbsp;&nbsp; 这样做在采用建模软件时得很机灵才行。总之，采用连接字段的方式<BR>&nbsp;&nbsp;&nbsp; 可以有效的隔离用户应用和开发人员界面。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 提防大小写混用的对象名和特殊字符<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 过去最令我恼火的事情之一就是数据库里有大小写混用的对象名，<BR>&nbsp;&nbsp;&nbsp; 比如 CustomerData。这一问题从 Access 到 Oracle 数据库都存在。<BR>&nbsp;&nbsp;&nbsp; 我不喜欢采用这种大小写混用的对象命名方法，结果还不得不手工修<BR>&nbsp;&nbsp;&nbsp; 改名字。想想看，这种数据库/应用程序能混到采用更强大数据库的<BR>&nbsp;&nbsp;&nbsp; 那一天吗？采用全部大写而且包含下划符的名字具有更好的可读性<BR>&nbsp;&nbsp;&nbsp; （CUSTOMER_DATA），绝对不要在对象名的字符之间留空格。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 小心保留词<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要保证你的字段名没有和保留词、数据库系统或者常用访问方法<BR>&nbsp;&nbsp;&nbsp; 冲突，比如，最近我编写的一个 ODBC 连接程序里有个表，其中就用<BR>&nbsp;&nbsp;&nbsp; 了 DESC 作为说明字段名。后果可想而知！DESC 是 DESCENDING 缩<BR>&nbsp;&nbsp;&nbsp; 写后的保留词。表里的一个 SELECT * 语句倒是能用，但我得到的却<BR>&nbsp;&nbsp;&nbsp; 是一大堆毫无用处的信息。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 保持字段名和类型的一致性<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在命名字段并为其指定数据类型的时候一定要保证一致性。假如<BR>&nbsp;&nbsp;&nbsp; 字段在某个表中叫做“ag reement_number”，你就别在另一个表里<BR>&nbsp;&nbsp;&nbsp; 把名字改成“ref1”。假如数据类型在一个表里是整数，那在另一个<BR>&nbsp;&nbsp;&nbsp; 表里可就别变成字符型了。记住，你干完自己的活了，其他人还要用<BR>&nbsp;&nbsp;&nbsp; 你的数据库呢。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 仔细选择数字类型<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在 SQL 中使用 smallint 和 tinyint 类型要特别小心，比如，<BR>&nbsp;&nbsp;&nbsp; 假如你想看看月销售总额，你的总额字段类型是 smallint，那么，<BR>&nbsp;&nbsp;&nbsp; 如果总额超过了$32,767 你就不能进行计算操作了。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 删除标记<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在表中包含一个“删除标记”字段，这样就可以把行标记为删除。<BR>&nbsp;&nbsp;&nbsp; 在关系数据库里不要单独删除某一行；最好采用清除数据程序而且要<BR>&nbsp;&nbsp;&nbsp; 仔细维护索引整体性。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 避免使用触发器<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 触发器的功能通常可以用其他方式实现。在调试程序时触发器可<BR>&nbsp;&nbsp;&nbsp; 能成为干扰。假如你确实需要采用触发器，你最好集中对它文档化。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 包含版本机制<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 建议你在数据库中引入版本控制机制来确定使用中的数据库的版<BR>&nbsp;&nbsp;&nbsp; 本。无论如何你都要实现这一要求。时间一长，用户的需求总是会改<BR>&nbsp;&nbsp;&nbsp; 变的。最终可能会要求修改数据库结构。虽然你可以通过检查新字段<BR>&nbsp;&nbsp;&nbsp; 或者索引来确定数据库结构的版本，但我发现把版本信息直接存放到<BR>&nbsp;&nbsp;&nbsp; 数据库中不更为方便吗？。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 给文本字段留足余量<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ID 类型的文本字段，比如客户 ID 或定单号等等都应该设置得<BR>&nbsp;&nbsp;&nbsp; 比一般想象更大，因为时间不长你多半就会因为要添加额外的字符而<BR>&nbsp;&nbsp;&nbsp; 难堪不已。比方说，假设你的客户 ID 为 10 位数长。那你应该把数<BR>&nbsp;&nbsp;&nbsp; 据库表字段的长度设为 12 或者 13 个字符长。这算浪费空间吗？是<BR>&nbsp;&nbsp;&nbsp; 有一点，但也没你想象的那么多：一个字段加长 3 个字符在有 1 百<BR>&nbsp;&nbsp;&nbsp; 万条记录，再加上一点索引的情况下才不过让整个数据库多占据 <BR>&nbsp;&nbsp;&nbsp; 3MB 的空间。但这额外占据的空间却无需将来重构整个数据库就可以<BR>&nbsp;&nbsp;&nbsp; 实现数据库规模的增长了。身份证的号码从 15 位变成 18 位就是最<BR>&nbsp;&nbsp;&nbsp; 好和最惨痛的例子。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 列[字段]命名技巧<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们发现，假如你给每个表的列[字段]名都采用统一的前缀，那<BR>&nbsp;&nbsp;&nbsp; 么在编写 SQL 表达式的时候会得到大大的简化。这样做也确实有缺<BR>&nbsp;&nbsp;&nbsp; 点，比如破坏了自动表连接工具的作用，后者把公共列[字段]名同某<BR>&nbsp;&nbsp;&nbsp; 些数据库联系起来，不过就连这些工具有时不也连接错误嘛。举个简<BR>&nbsp;&nbsp;&nbsp; 单的例子，假设有两个表：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Customer 和 Order。Customer 表的前缀是 cu_，所以该表内的<BR>&nbsp;&nbsp;&nbsp; 子段名如下：cu_name_id 、cu_surname、cu_initials 和<BR>&nbsp;&nbsp;&nbsp; cu_address 等。Order 表的前缀是 or_，所以子段名是：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or_order_id、or_cust_name_id、or_quantity 和 <BR>&nbsp;&nbsp;&nbsp; or_description 等。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样从数据库中选出全部数据的 SQL 语句可以写成如下所示：<BR><BR>&nbsp;&nbsp;&nbsp; _______________________<BR>&nbsp;&nbsp;&nbsp; Select * From Customer, Order <BR>&nbsp;&nbsp;&nbsp; Where cu_surname = "MYNAME"<BR>&nbsp;&nbsp;&nbsp; and cu_name_id = or_cust_name_id and or_quantity = 1 <BR>&nbsp;&nbsp;&nbsp; _______________________<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在没有这些前缀的情况下则写成这个样子（用别名来区分）：<BR><BR>&nbsp;&nbsp;&nbsp; _______________________<BR>&nbsp;&nbsp;&nbsp; Select * From Customer, Order <BR>&nbsp;&nbsp;&nbsp; Where Customer.surname = "MYNAME" <BR>&nbsp;&nbsp;&nbsp; and Customer.name_id = Order.cust_name_id <BR>&nbsp;&nbsp;&nbsp; and Order.quantity = 1&nbsp; <BR>&nbsp;&nbsp;&nbsp; _______________________<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第 1 个 SQL 语句没少键入多少字符。但如果查询涉及到 5 个<BR>&nbsp;&nbsp;&nbsp; 表乃至更多的列[字段]你就知道这个技巧多有用了。<BR><BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; § 第 3 部分 - 选择键和索引<BR>&nbsp;&nbsp;&nbsp; ──────────────<BR><BR>&nbsp;&nbsp;&nbsp; ■ 数据采掘要预先计划<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我所在的某一客户部门一度要处理 8 万多份联系方式，同时填<BR>&nbsp;&nbsp;&nbsp; 写每个客户的必要数据（这绝对不是小活）。我从中还要确定出一组<BR>&nbsp;&nbsp;&nbsp; 客户作为市场目标。当我从最开始设计表和字段的时候，我试图不在<BR>&nbsp;&nbsp;&nbsp; 主索引里增加太多的字段以便加快数据库的运行速度。然后我意识到<BR>&nbsp;&nbsp;&nbsp; 特定的组查询和信息采掘既不准确速度也不快。结果只好在主索引中<BR>&nbsp;&nbsp;&nbsp; 重建而且合并了数据字段。我发现有一个指示计划相当关键——当我<BR>&nbsp;&nbsp;&nbsp; 想创建系统类型查找时为什么要采用号码作为主索引字段呢？我可以<BR>&nbsp;&nbsp;&nbsp; 用传真号码进行检索，但是它几乎就象系统类型一样对我来说并不重<BR>&nbsp;&nbsp;&nbsp; 要。采用后者作为主字段，数据库更新后重新索引和检索就快多了。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可操作数据仓库（ODS）和数据仓库（DW）这两种环境下的数据<BR>&nbsp;&nbsp;&nbsp; 索引是有差别的。在 DW 环境下，你要考虑销售部门是如何组织销售<BR>&nbsp;&nbsp;&nbsp; 活动的。他们并不是数据库管理员，但是他们确定表内的键信息。这<BR>&nbsp;&nbsp;&nbsp; 里设计人员或者数据库工作人员应该分析数据库结构从而确定出性能<BR>&nbsp;&nbsp;&nbsp; 和正确输出之间的最佳条件。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 使用系统生成的主键<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这类同技巧 1，但我觉得有必要在这里重复提醒大家。假如你总<BR>&nbsp;&nbsp;&nbsp; 是在设计数据库的时候采用系统生成的键作为主键，那么你实际控制<BR>&nbsp;&nbsp;&nbsp; 了数据库的索引完整性。这样，数据库和非人工机制就有效地控制了<BR>&nbsp;&nbsp;&nbsp; 对存储数据中每一行的访问。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 采用系统生成键作为主键还有一个优点：当你拥有一致的键结构<BR>&nbsp;&nbsp;&nbsp; 时，找到逻辑缺陷很容易。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 分解字段用于索引<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了分离命名字段和包含字段以支持用户定义的报表，请考虑分<BR>&nbsp;&nbsp;&nbsp; 解其他字段（甚至主键）<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为其组成要素以便用户可以对其进行索引。索引将加快 SQL 和<BR>&nbsp;&nbsp;&nbsp; 报表生成器脚本的执行速度。比方说，我通常在必须使用 SQL <BR>&nbsp;&nbsp;&nbsp; LIKE 表达式的情况下创建报表，因为 case number 字段无法分解为 <BR>&nbsp;&nbsp;&nbsp; year、serial number、case type 和 defendant code 等要素。性<BR>&nbsp;&nbsp;&nbsp; 能也会变坏。假如年度和类型字段可以分解为索引字段那么这些报表<BR>&nbsp;&nbsp;&nbsp; 运行起来就会快多了。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 键设计 4 原则<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; 1. 为关联字段创建外键。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 2. 所有的键都必须唯一。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 3. 避免使用复合键。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 4. 外键总是关联唯一的键字段。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; ■ 别忘了索引<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 索引是从数据库中获取数据的最高效方式之一。95%的数据库性<BR>&nbsp;&nbsp;&nbsp; 能问题都可以采用索引技术得到解决。作为一条规则，我通常对逻辑<BR>&nbsp;&nbsp;&nbsp; 主键使用唯一的成组索引，对系统键（作为存储过程）采用唯一的非<BR>&nbsp;&nbsp;&nbsp; 成组索引，对任何外键列[字段]采用非成组索引。不过，索引就象是<BR>&nbsp;&nbsp;&nbsp; 盐，太多了菜就咸了。你得考虑数据库的空间有多大，表如何进行访<BR>&nbsp;&nbsp;&nbsp; 问，还有这些访问是否主要用作读写。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 大多数数据库都索引自动创建的主键字段，但是可别忘了索引外<BR>&nbsp;&nbsp;&nbsp; 键，它们也是经常使用的键，比如运行查询显示主表和所有关联表的<BR>&nbsp;&nbsp;&nbsp; 某条记录就用得上。还有，不要索引 memo/no te 字段，不要索引大<BR>&nbsp;&nbsp;&nbsp; 型字段（有很多字符），这样作会让索引占用太多的存储空间。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 不要索引常用的小型表<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不要为小型数据表设置任何键，假如它们经常有插入和删除操作<BR>&nbsp;&nbsp;&nbsp; 就更别这样作了。对这些插入和删除操作的索引维护可能比扫描表空<BR>&nbsp;&nbsp;&nbsp; 间消耗更多的时间。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不要把社会保障号码（SSN）或身份证号码（ID）选作键永远都<BR>&nbsp;&nbsp;&nbsp; 不要使用 SSN 或 ID 作为数据库的键。除了隐私原因以外，须知政<BR>&nbsp;&nbsp;&nbsp; 府越来越趋向于不准许把 SSN 或 ID 用作除收入相关以外的其他目<BR>&nbsp;&nbsp;&nbsp; 的，SSN 或 ID 需要手工输入。永远不要使用手工输入的键作为主键，<BR>&nbsp;&nbsp;&nbsp; 因为一旦你输入错误，你唯一能做的就是删除整个记录然后从头开始。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我在破解他人的程序时候，我看到很多人把 SSN 或 ID 还曾被<BR>&nbsp;&nbsp;&nbsp; 用做系列号，当然尽管这么做是非法的。而且人们也都知道这是非法<BR>&nbsp;&nbsp;&nbsp; 的，但他们已经习惯了。后来，随着盗取身份犯罪案件的增加，我现<BR>&nbsp;&nbsp;&nbsp; 在的同行正痛苦地从一大摊子数据中把 SSN 或 ID 删除。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 不要用用户的键<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在确定采用什么字段作为表的键的时候，可一定要小心用户将要<BR>&nbsp;&nbsp;&nbsp; 编辑的字段。通常的情况下不要选择用户可编辑的字段作为键。这样<BR>&nbsp;&nbsp;&nbsp; 做会迫使你采取以下两个措施：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; 1. 在创建记录之后对用户编辑字段的行为施加限制。假如你这么做<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 了，你可能会发现你的应用程序在商务需求突然发生变化，而用<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 户需要编辑那些不可编辑的字段时缺乏足够的灵活性。当用户在<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入数据之后直到保存记录才发现系统出了问题他们该怎么想？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 删除重建？假如记录不可重建是否让用户走开？<BR>&nbsp;&nbsp;&nbsp;&nbsp; 2. 提出一些检测和纠正键冲突的方法。通常，费点精力也就搞定了，<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但是从性能上来看这样做的代价就比较大了。还有，键的纠正可<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 能会迫使你突破你的数据和商业/用户界面层之间的隔离。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以还是重提一句老话：你的设计要适应用户而不是让用户来适<BR>&nbsp;&nbsp;&nbsp; 应你的设计。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不让主键具有可更新性的原因是在关系模式下，主键实现了不同<BR>&nbsp;&nbsp;&nbsp; 表之间的关联。比如，Cu stomer 表有一个主键 CustomerID，而客<BR>&nbsp;&nbsp;&nbsp; 户的定单则存放在另一个表里。Order 表的主键可能是 OrderNo 或<BR>&nbsp;&nbsp;&nbsp; 者 OrderNo、CustomerID 和日期的组合。不管你选择哪种键设置，<BR>&nbsp;&nbsp;&nbsp; 你都需要在 Order 表中存放 CustomerID 来保证你可以给下定单的<BR>&nbsp;&nbsp;&nbsp; 用户找到其定单记录。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 假如你在 Customer 表里修改了 CustomerID，那么你必须找出 <BR>&nbsp;&nbsp;&nbsp; Order 表中的所有相关记录对其进行修改。否则，有些定单就会不属<BR>&nbsp;&nbsp;&nbsp; 于任何客户——数据库的完整性就算完蛋了。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果索引完整性规则施加到表一级，那么在不编写大量代码和附<BR>&nbsp;&nbsp;&nbsp; 加删除记录的情况下几乎不可能改变某一条记录的键和数据库内所有<BR>&nbsp;&nbsp;&nbsp; 关联的记录。而这一过程往往错误丛生所以应该尽量避免。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 可选键(候选键)有时可做主键<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 记住，查询数据的不是机器而是人。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 假如你有可选键，你可能进一步把它用做主键。那样的话，你就<BR>&nbsp;&nbsp;&nbsp; 拥有了建立强大索引的能力。这样可以阻止使用数据库的人不得不连<BR>&nbsp;&nbsp;&nbsp; 接数据库从而恰当的过滤数据。在严格控制域表的数据库上，这种负<BR>&nbsp;&nbsp;&nbsp; 载是比较醒目的。如果可选键真正有用，那就是达到了主键的水准。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我的看法是，假如你有可选键，比如国家表内的 state_code，<BR>&nbsp;&nbsp;&nbsp; 你不要在现有不能变动的唯一键上创建后续的键。你要做的无非是创<BR>&nbsp;&nbsp;&nbsp; 建毫无价值的数据。如你因为过度使用表的后续键[别名]建立这种表<BR>&nbsp;&nbsp;&nbsp; 的关联，操作负载真得需要考虑一下了。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 别忘了外键<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 大多数数据库索引自动创建的主键字段。但别忘了索引外键字段，<BR>&nbsp;&nbsp;&nbsp; 它们在你想查询主表中的记录及其关联记录时每次都会用到。还有，<BR>&nbsp;&nbsp;&nbsp; 不要索引 memo/notes 字段而且不要索引大型文本字段（许多字符），<BR>&nbsp;&nbsp;&nbsp; 这样做会让你的索引占据大量的数据库空间。<BR><BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; § 第 4 部分 - 保证数据的完整性<BR>&nbsp;&nbsp;&nbsp; ──────────────<BR><BR>&nbsp;&nbsp;&nbsp; ■ 用约束而非商务规则强制数据完整性<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你按照商务规则来处理需求，那么你应当检查商务层次/用<BR>&nbsp;&nbsp;&nbsp; 户界面：如果商务规则以后发生变化，那么只需要进行更新即可。假<BR>&nbsp;&nbsp;&nbsp; 如需求源于维护数据完整性的需要，那么在数据库层面上需要施加限<BR>&nbsp;&nbsp;&nbsp; 制条件。如果你在数据层确实采用了约束，你要保证有办法把更新不<BR>&nbsp;&nbsp;&nbsp; 能通过约束检查的原因采用用户理解的语言通知用户界面。除非你的<BR>&nbsp;&nbsp;&nbsp; 字段命名很冗长，否则字段名本身还不够。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只要有可能，请采用数据库系统实现数据的完整性。这不但包括<BR>&nbsp;&nbsp;&nbsp; 通过标准化实现的完整性而且还包括数据的功能性。在写数据的时候<BR>&nbsp;&nbsp;&nbsp; 还可以增加触发器来保证数据的正确性。不要依赖于商务层保证数据<BR>&nbsp;&nbsp;&nbsp; 完整性；它不能保证表之间（外键）的完整性所以不能强加于其他完<BR>&nbsp;&nbsp;&nbsp; 整性规则之上。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 分布式数据系统<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对分布式系统而言，在你决定是否在各个站点复制所有数据还是<BR>&nbsp;&nbsp;&nbsp; 把数据保存在一个地方之前应该估计一下未来 5 年或者 10 年的数<BR>&nbsp;&nbsp;&nbsp; 据量。当你把数据传送到其他站点的时候，最好在数据库字段中设置<BR>&nbsp;&nbsp;&nbsp; 一些标记。在目的站点收到你的数据之后更新你的标记。为了进行这<BR>&nbsp;&nbsp;&nbsp; 种数据传输，请写下你自己的批处理或者调度程序以特定时间间隔运<BR>&nbsp;&nbsp;&nbsp; 行而不要让用户在每天的工作后传输数据。本地拷贝你的维护数据，<BR>&nbsp;&nbsp;&nbsp; 比如计算常数和利息率等，设置版本号保证数据在每个站点都完全一<BR>&nbsp;&nbsp;&nbsp; 致。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 强制指示完整性(参照完整性?)<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没有好办法能在有害数据进入数据库之后消除它，所以你应该在<BR>&nbsp;&nbsp;&nbsp; 它进入数据库之前将其剔除。激活数据库系统的指示完整性特性。这<BR>&nbsp;&nbsp;&nbsp; 样可以保持数据的清洁而能迫使开发人员投入更多的时间处理错误条<BR>&nbsp;&nbsp;&nbsp; 件。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 关系<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果两个实体之间存在多对一关系，而且还有可能转化为多对多<BR>&nbsp;&nbsp;&nbsp; 关系，那么你最好一开始就设置成多对多关系。从现有的多对一关系<BR>&nbsp;&nbsp;&nbsp; 转变为多对多关系比一开始就是多对多关系要难得多。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 采用视图<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了在你的数据库和你的应用程序代码之间提供另一层抽象，你<BR>&nbsp;&nbsp;&nbsp; 可以为你的应用程序建立专门的视图而不必非要应用程序直接访问数<BR>&nbsp;&nbsp;&nbsp; 据表。这样做还等于在处理数据库变更时给你提供了更多的自由。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 给数据保有和恢复制定计划<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 考虑数据保有策略并包含在设计过程中，预先设计你的数据恢复<BR>&nbsp;&nbsp;&nbsp; 过程。采用可以发布给用户/开发人员的数据字典实现方便的数据识<BR>&nbsp;&nbsp;&nbsp; 别同时保证对数据源文档化。编写在线更新来“更新查询”供以后万<BR>&nbsp;&nbsp;&nbsp; 一数据丢失可以重新处理更新。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 用存储过程让系统做重活<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解决了许多麻烦来产生一个具有高度完整性的数据库解决方案之<BR>&nbsp;&nbsp;&nbsp; 后，我决定封装一些关联表的功能组，提供一整套常规的存储过程来<BR>&nbsp;&nbsp;&nbsp; 访问各组以便加快速度和简化客户程序代码的开发。数据库不只是一<BR>&nbsp;&nbsp;&nbsp; 个存放数据的地方，它也是简化编码之地。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 使用查找<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 控制数据完整性的最佳方式就是限制用户的选择。只要有可能都<BR>&nbsp;&nbsp;&nbsp; 应该提供给用户一个清晰的价值列表供其选择。这样将减少键入代码<BR>&nbsp;&nbsp;&nbsp; 的错误和误解同时提供数据的一致性。某些公共数据特别适合查找：<BR>&nbsp;&nbsp;&nbsp; 国家代码、状态代码等。<BR><BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; § 第 5 部分 - 各种小技巧<BR>&nbsp;&nbsp;&nbsp; ──────────────<BR><BR>&nbsp;&nbsp;&nbsp; ■ 文档、文档、文档<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对所有的快捷方式、命名规范、限制和函数都要编制文档。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 采用给表、列[字段]、触发器等加注释的数据库工具。是的，这<BR>&nbsp;&nbsp;&nbsp; 有点费事，但从长远来看，这样做对开发、支持和跟踪修改非常有用。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取决于你使用的数据库系统，可能有一些软件会给你一些供你很<BR>&nbsp;&nbsp;&nbsp; 快上手的文档。你可能希望先开始在说，然后获得越来越多的细节。<BR>&nbsp;&nbsp;&nbsp; 或者你可能希望周期性的预排，在输入新数据同时随着你的进展对每<BR>&nbsp;&nbsp;&nbsp; 一部分细节化。不管你选择哪种方式，总要对你的数据库文档化，或<BR>&nbsp;&nbsp;&nbsp; 者在数据库自身的内部或者单独建立文档。这样，当你过了一年多时<BR>&nbsp;&nbsp;&nbsp; 间后再回过头来做第 2 个版本，你犯错的机会将大大减少。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用常用英语（或者其他任何语言）而不要使用编码为什么我们<BR>&nbsp;&nbsp;&nbsp; 经常采用编码（比如 9935A 可能是‘青岛啤酒’的供应代码，<BR>&nbsp;&nbsp;&nbsp; 4XF788-Q 可能是帐目编码）？理由很多。但是用户通常都用英语进<BR>&nbsp;&nbsp;&nbsp; 行思考而不是编码。工作 5 年的会计或许知道 4XF788-Q 是什么东<BR>&nbsp;&nbsp;&nbsp; 西，但新来的可就不一定了。在创建下拉菜单、列表、报表时最好按<BR>&nbsp;&nbsp;&nbsp; 照英语名排序。假如你需要编码，那你可以在编码旁附上用户知道的<BR>&nbsp;&nbsp;&nbsp; 英语。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 保存常用信息<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 让一个表专门存放一般数据库信息非常有用。我常在这个表里存<BR>&nbsp;&nbsp;&nbsp; 放数据库当前版本、最近检查/修复（对 FoxPro）、关联设计文档的<BR>&nbsp;&nbsp;&nbsp; 名称、客户等信息。这样可以实现一种简单机制跟踪数据库，当客户<BR>&nbsp;&nbsp;&nbsp; 抱怨他们的数据库没有达到希望的要求而与你联系时，这样做对非客<BR>&nbsp;&nbsp;&nbsp; 户机/服务器环境特别有用。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 测试、测试、反复测试<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 建立或者修订数据库之后，必须用用户新输入的数据测试数据字<BR>&nbsp;&nbsp;&nbsp; 段。最重要的是，让用户进行测试并且同用户一道保证你选择的数据<BR>&nbsp;&nbsp;&nbsp; 类型满足商业要求。测试需要在把新数据库投入实际服务之前完成。<BR><BR>&nbsp;&nbsp;&nbsp; ■ 检查设计<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在开发期间检查数据库设计的常用技术是通过其所支持的应用程<BR>&nbsp;&nbsp;&nbsp; 序原型检查数据库。换句话说，针对每一种最终表达数据的原型应用，<BR>&nbsp;&nbsp;&nbsp; 保证你检查了数据模型并且查看如何取出数据。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Microsoft Visual FoxPro 设计技巧对复杂的 Microsoft <BR>&nbsp;&nbsp;&nbsp; Visual FoxPro 数据库应用程序而言，可以把所有的主表放在一个数<BR>&nbsp;&nbsp;&nbsp; 据库容器文件里，然后增加其他数据库表文件和装载同原有数据库有<BR>&nbsp;&nbsp;&nbsp; 关的特殊文件。根据需要用这些文件连接到主文件中的主表。比如数<BR>&nbsp;&nbsp;&nbsp; 据输入、数据索引、统计分析、向管理层或者政府部门提供报表以及<BR>&nbsp;&nbsp;&nbsp; 各类只读查询等。这一措施简化了用户和组权限的分配，而且有利于<BR>&nbsp;&nbsp;&nbsp; 应用程序函数（存储过程）的分组和划分，从而在程序必须修改的时<BR>&nbsp;&nbsp;&nbsp; 候易于管理。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 谨把本文献给战斗在狐狸战线上的同志们！！！<BR><BR><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;&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; -- 夜来香<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;&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; 2002/05/30<BR><BR></P>]]></description>
</item>
</channel>
</rss>