一個(gè)成功的管理系統,是由:[50% 的業(yè)務(wù) + 50% 的軟件] 所組成,而 50% 的成功軟件又有 [25% 的數據庫 + 25%的程序] 所組成,數據庫設計的好壞是一個(gè)關(guān)鍵。如果把企業(yè)的數據比做生命 所必需的血液,那么數據庫的設計就是應用中最重要的一部分 。有關(guān)數據庫設計的材料汗牛充棟,大學(xué)學(xué)位課程里也有專(zhuān)門(mén)的講述 。不過(guò),就如我們反復強調的那樣,再好的老師也比不過(guò)經(jīng)驗的教誨 。所以我歸納歷年來(lái)所走的彎路及體會(huì ),并在網(wǎng)上找了些對數據庫設 計頗有造詣的專(zhuān)業(yè)人士給大家傳授一些設計數據庫的技巧和經(jīng)驗 。精選了其中的60 個(gè)最佳技巧,并把這些技巧編寫(xiě)成了本文,為了方便索引其內容劃分 為 5 個(gè)部分:
第 1 部分 - 設計數據庫之前
這一部分羅列了 12 個(gè)基本技巧,包括命名規范和明確業(yè)務(wù)需求等。
第 2 部分 - 設計數據庫表
總共 24 個(gè)指南性技巧,涵蓋表內字段設計以及應該避免的常見(jiàn)問(wèn)題等。
第 3 部分 - 選擇鍵
怎么選擇鍵呢?這里有 10 個(gè)技巧專(zhuān)門(mén)涉及系統生成的主鍵的正確用法,還有何 時(shí)以及如何索引字段以獲得最佳性能等。
第 4 部分 - 保證數據完整性
討論如何保持數據庫的清晰和健壯,如何把有害數據降低到最小程度 。
第 5 部分 - 各種小技巧
不包括在以上 4 個(gè)部分中的其他技巧,五花八門(mén),有了它們希望你的數據庫開(kāi)發(fā)工作 會(huì )更輕松一些。
第 1 部分 - 設計數據庫之前
考察現有環(huán)境
在設計一個(gè)新數據庫時(shí),你不但應該仔細研究業(yè)務(wù)需求而且還要考察 現有的系統。大多數數據庫項目都不是從頭開(kāi)始建立的;通常 ,機構內總會(huì )存在用來(lái)滿(mǎn)足特定需求的現有系統(可能沒(méi)有實(shí)現自動(dòng) 計算)。顯然,現有系統并不完美,否則你就不必再建立新系統了 。但是對舊系統的研究可以讓你發(fā)現一些可能會(huì )忽略的細微問(wèn)題 。一般來(lái)說(shuō),考察現有系統對你絕對有好處。
定義標準的對象命名規范
一定要定義數據庫對象的命名規范。對數據庫表來(lái)說(shuō) ,從項目一開(kāi)始就要確定表名是采用復數還是單數形式 。此外還要給表的別名定義簡(jiǎn)單規則(比方說(shuō),如果表名是一個(gè)單詞 ,別名就取單詞的前4 個(gè)字母;如果表名是兩個(gè)單詞,就各取兩個(gè)單詞的前兩個(gè)字母組成 4 個(gè)字母長(cháng)的別名;如果表的名字由 3個(gè)單詞組成,你不妨從頭兩個(gè)單詞中各取一個(gè)然后從最后一個(gè)單詞中 再取出兩個(gè)字母,結果還是組成 4字母長(cháng)的別名,其余依次類(lèi)推)對工作用表來(lái)說(shuō),表名可以加上前綴 WORK_后面附上采用該表的應用程序的名字。表內的列[字段 ]要針對鍵采用一整套設計規則。比如,如果鍵是數字類(lèi)型 ,你可以用 _N作為后綴;如果是字符類(lèi)型則可以采用 _C后綴。對列[字段]名應該采用標準的前綴和后綴。再如 ,假如你的表里有好多"money"字段,你不妨給每個(gè)列[字段 ]增加一個(gè) _M后綴。還有,日期列[字段]最好以 D_ 作為名字打頭。
檢查表名、報表名和查詢(xún)名之間的命名規范。你可能會(huì )很快就被這些 不同的數據庫要素的名稱(chēng)搞糊涂了。假如你堅持統一地命名這些數據 庫的不同組成部分,至少你應該在這些對象名字的開(kāi)頭用Table、Query 或者 Report 等前綴加以區別。
如果采用了 Microsoft Access,你可以用 qry、rpt、tbl 和 mod 等符號來(lái)標識對象(比如tbl_Employees)。我在和 SQL Server 打交道的時(shí)候還用過(guò) tbl 來(lái)索引表,但我用 sp_company (現在用 sp_feft_)標識存儲過(guò)程,因為在有的時(shí)候如果我發(fā)現了更 好的處理辦法往往會(huì )保存好幾個(gè)拷貝。我在實(shí)現 SQL Server 2000 時(shí)用udf_ (或者類(lèi)似的標記)標識我編寫(xiě)的函數。
工欲善其事, 必先利其器
采用理想的數據庫設計工具,比如:SyBase 公司的 PowerDesign,她支持 PB、VB、Delphe 等語(yǔ)言,通過(guò) ODBC可以連接市面上流行的 30 多個(gè)數據庫,包括 dBase、FoxPro、VFP、SQL Server 等,今后有機會(huì )我將著(zhù)重介紹PowerDesign 的使用。
獲取數據模式資源手冊
正在尋求示例模式的人可以閱讀《數據模式資源手冊》一書(shū),該書(shū)由 Len Silverston、W. H. Inmon 和 Kent Graziano 編寫(xiě),是一本值得擁有的最佳數據建模圖書(shū)。該書(shū)包括的章節涵蓋多 種數據領(lǐng)域,比如人員、機構和工作效能等。其他的你還可以參考: [1]薩師煊王珊著(zhù)數據庫系統概論(第二版)高等教育出版社 1991、[2][美] Steven M.Bobrowski 著(zhù) Oracle 7
與客戶(hù)/服務(wù)器計算技術(shù)從入門(mén)到精通劉建元等譯電子工業(yè)出版社,1996、[3]周中元信息系統建模方法(下)電子與信息化1999年第3期,1999暢想未來(lái),但不可忘了過(guò)去的教訓我發(fā)現詢(xún)問(wèn)用戶(hù)如何看待未來(lái)需求變化非常有用。這樣做可以達到兩 個(gè)目的:首先,你可以清楚地了解應用設計在哪個(gè)地方應該更具靈活 性以及如何避免性能瓶頸;其次,你知道發(fā)生事先沒(méi)有確定的需求變 更時(shí)用戶(hù)將和你一樣感到吃驚。
一定要記住過(guò)去的經(jīng)驗教訓!我們開(kāi)發(fā)人員還應該通過(guò)分享自己的體 會(huì )和經(jīng)驗互相幫助。即使用戶(hù)認為他們再也不需要什么支持了 ,我們也應該對他們進(jìn)行這方面的教育,我們都曾經(jīng)面臨過(guò)這樣的時(shí) 刻"當初要是這么做了該多好.."。
在物理實(shí)踐之前進(jìn)行邏輯設計
在深入物理設計之前要先進(jìn)行邏輯設計。隨著(zhù)大量的 CASE工具不斷涌現出來(lái),你的設計也可以達到相當高的邏輯水準 ,你通??梢詮恼w上更好地了解數據庫設計所需要的方方面面。
了解你的業(yè)務(wù)
在你百分百地確定系統從客戶(hù)角度滿(mǎn)足其需求之前不要在你的 ER(實(shí)體關(guān)系)模式中加入哪怕一個(gè)數據表(怎么,你還沒(méi)有模式 ?那請你參看技巧9)。了解你的企業(yè)業(yè)務(wù)可以在以后的開(kāi)發(fā)階段節約大量的時(shí)間 。一旦你明確了業(yè)務(wù)需求,你就可以自己做出許多決策了。
一旦你認為你已經(jīng)明確了業(yè)務(wù)內容,你最好同客戶(hù)進(jìn)行一次系統的交 流。采用客戶(hù)的術(shù)語(yǔ)并且向他們解釋你所想到的和你所聽(tīng)到的 。同時(shí)還應該用可能、將會(huì )和必須等詞匯表達出系統的關(guān)系基數 。這樣你就可以讓你的客戶(hù)糾正你自己的理解然后做好下一步的ER 設計。
創(chuàng )建數據字典和 ER 圖表
一定要花點(diǎn)時(shí)間創(chuàng )建 ER 圖表和數據字典。其中至少應該包含每個(gè)字段的數據類(lèi)型和在每個(gè)表 內的主外鍵。創(chuàng )建 ER
圖表和數據字典確實(shí)有點(diǎn)費時(shí)但對其他開(kāi)發(fā)人員要了解整個(gè)設計卻是 完全必要的。越早創(chuàng )建越能有助于避免今后面臨的可能混亂 ,從而可以讓任何了解數據庫的人都明確如何從數據庫中獲得數據。
有一份諸如 ER 圖表等最新文檔其重要性如何強調都不過(guò)分,這對表明表之間關(guān)系很 有用,而數據字典則說(shuō)明了每個(gè)字段的用途以及任何可能存在的別名 。對SQL 表達式的文檔化來(lái)說(shuō)這是完全必要的。
創(chuàng )建模式
一張圖表勝過(guò)千言萬(wàn)語(yǔ):開(kāi)發(fā)人員不僅要閱讀和實(shí)現它 ,而且還要用它來(lái)幫助自己和用戶(hù)對話(huà)。模式有助于提高協(xié)作效能 ,這樣在先期的數據庫設計中幾乎不可能出現大的問(wèn)題 。模式不必弄的很復雜;甚至可以簡(jiǎn)單到手寫(xiě)在一張紙上就可以了 。只是要保證其上的邏輯關(guān)系今后能產(chǎn)生效益。
從輸入輸出下手
在定義數據庫表和字段需求(輸入)時(shí),首先應檢查現有的或者已經(jīng) 設計出的報表、查詢(xún)和視圖(輸出)以決定為了支持這些輸出哪些是 必要的表和字段。舉個(gè)簡(jiǎn)單的例子:假如客戶(hù)需要一個(gè)報表按照郵政 編碼排序、分段和求和,你要保證其中包括了單獨的郵政編碼字段而 不要把郵政編碼糅進(jìn)地址字段里。
報表技巧
要了解用戶(hù)通常是如何報告數據的:批處理還是在線(xiàn)提交報表 ?時(shí)間間隔是每天、每周、每月、每個(gè)季度還是每年 ?如果需要的話(huà)還可以考慮創(chuàng )建總結表。系統生成的主鍵在報表中很 難管理。用戶(hù)在具有系統生成主鍵的表內用副鍵進(jìn)行檢索往往會(huì )返回 許多重復數據。這樣的檢索性能比較低而且容易引起混亂。
理解客戶(hù)需求
看起來(lái)這應該是顯而易見(jiàn)的事,但需求就是來(lái)自客戶(hù) (這里要從內部和外部客戶(hù)的角度考慮)。不要依賴(lài)用戶(hù)寫(xiě)下來(lái)的需 求,真正的需求在客戶(hù)的腦袋里。你要讓客戶(hù)解釋其需求 ,而且隨著(zhù)開(kāi)發(fā)的繼續,還要經(jīng)常詢(xún)問(wèn)客戶(hù)保證其需求仍然在開(kāi)發(fā)的 目的之中。一個(gè)不變的真理是:"只有我看見(jiàn)了我才知道我想要的是 什么"必然會(huì )導致大量的返工,因為數據庫沒(méi)有達到客戶(hù)從來(lái)沒(méi)有寫(xiě) 下來(lái)的需求標準。而更糟的是你對他們需求的解釋只屬于你自己 ,而且可能是完全錯誤的。
第 2 部分 - 設計表和字段
檢查各種變化
我在設計數據庫的時(shí)候會(huì )考慮到哪些數據字段將來(lái)可能會(huì )發(fā)生變更 。比方說(shuō),姓氏就是如此(注意是西方人的姓氏,比如女性結婚后從 夫姓等)。所以,在建立系統存儲客戶(hù)信息時(shí),我傾向于在單獨的一 個(gè)數據表里存儲姓氏字段,而且還附加起始日和終止日等字段 ,這樣就可以跟蹤這一數據條目的變化。
采用有意義的字段名
有一回我參加開(kāi)發(fā)過(guò)一個(gè)項目,其中有從其他程序員那里繼承的程序 ,那個(gè)程序員喜歡用屏幕上顯示數據指示用語(yǔ)命名字段,這也不賴(lài) ,但不幸的是,她還喜歡用一些奇怪的命名法,其命名采用了匈牙利 命名和控制序號的組合形式,比如cbo1、txt2、txt2_b 等等。
除非你在使用只面向你的縮寫(xiě)字段名的系統,否則請盡可能地把字段 描述的清楚些。當然,也別做過(guò)頭了,比如Customer_Shipping_Address _Street_Line_1,雖然很富有說(shuō)明性 ,但沒(méi)人愿意鍵入這么長(cháng)的名字,具體尺度就在你的把握中。
采用前綴命名。如果多個(gè)表里有好多同一類(lèi)型的字段(比如 FirstName),你不妨用特定表的前綴(比如 CusLastName)來(lái)幫助你標識字段。
時(shí)效性數據應包括"最近更新日期/時(shí)間"字段。時(shí)間標記對查找數 據問(wèn)題的原因、按日期重新處理/重載數據和清除舊數據特別有用。
標準化和數據驅動(dòng)數據的標準化不僅方便了自己而且也方便了其他人。比方說(shuō) ,假如你的用戶(hù)界面要訪(fǎng)問(wèn)外部數據源(文件、XML
文檔、其他數據庫等),你不妨把相應的連接和路徑信息存儲在用戶(hù) 界面支持表里。還有,如果用戶(hù)界面執行工作流之類(lèi)的任務(wù) (發(fā)送郵件、打印信箋、修改記錄狀態(tài)等),那么產(chǎn)生工作流的數據 也可以存放在數據庫里。預先安排總需要付出努力,但如果這些過(guò)程 采用數據驅動(dòng)而非硬編碼的方式,那么策略變更和維護都會(huì )方便得多 。事實(shí)上,如果過(guò)程是數據驅動(dòng)的,你就可以把相當大的責任推給用 戶(hù),由用戶(hù)來(lái)維護自己的工作流過(guò)程。
標準化不能過(guò)頭
對那些不熟悉標準化一詞(normalization)的人而言 ,標準化可以保證表內的字段都是最基礎的要素,而這一措施有助于 消除數據庫中的數據冗余。標準化有好幾種形式,但Third Normal Form(3NF)通常被認為在性能、擴展性和數據完整性方面達 到了最好平衡。簡(jiǎn)單來(lái)說(shuō),3NF 規定:
* 表內的每一個(gè)值都只能被表達一次。
* 表內的每一行都應該被唯一的標識(有唯一鍵)。
* 表內不應該存儲依賴(lài)于其他鍵的非鍵信息。
遵守 3NF 標準的數據庫具有以下特點(diǎn):有一組表專(zhuān)門(mén)存放通過(guò)鍵連接起來(lái)的關(guān) 聯(lián)數據。比方說(shuō),某個(gè)存放客戶(hù)及其有關(guān)定單的 3NF
數據庫就可能有兩個(gè)表:Customer 和 Order。Order 表不包含定單關(guān)聯(lián)客戶(hù)的任何信息,但表內會(huì )存放一個(gè)鍵值 ,該鍵指向Customer 表里包含該客戶(hù)信息的那一行。
更高層次的標準化也有,但更標準是否就一定更好呢?答案是不一定 。事實(shí)上,對某些項目來(lái)說(shuō),甚至就連 3NF 都可能給數據庫引入太高的復雜性。
為了效率的緣故,對表不進(jìn)行標準化有時(shí)也是必要的 ,這樣的例子很多。曾經(jīng)有個(gè)開(kāi)發(fā)餐飲分析軟件的活就是用非標準化 表把查詢(xún)時(shí)間從平均 40秒降低到了兩秒左右。雖然我不得不這么做,但我絕不把數據表的非 標準化當作當然的設計理念。而具體的操作不過(guò)是一種派生 。所以如果表出了問(wèn)題重新產(chǎn)生非標準化的表是完全可能的。
Microsoft Visual FoxPro 報表技巧
如果你正在使用 Microsoft Visual FoxPro,你可以用對用戶(hù)友好的字段名來(lái)代替編號的名稱(chēng) :比如用 Customer Name 代替 txtCNaM。這樣,當你用向導程序 [Wizards,臺灣人稱(chēng)為‘精靈‘] 創(chuàng )建表單和報表時(shí),其名字會(huì )讓那些不是程序員的人更容易閱讀。
不活躍或者不采用的指示符
增加一個(gè)字段表示所在記錄是否在業(yè)務(wù)中不再活躍挺有用的 。不管是客戶(hù)、員工還是其他什么人,這樣做都能有助于再運行查詢(xún) 的時(shí)候過(guò)濾活躍或者不活躍狀態(tài)。同時(shí)還消除了新用戶(hù)在采用數據時(shí) 所面臨的一些問(wèn)題,比如,某些記錄可能不再為他們所用 ,再刪除的時(shí)候可以起到一定的防范作用。
使用角色實(shí)體定義屬于某類(lèi)別的列[字段]
在需要對屬于特定類(lèi)別或者具有特定角色的事物做定義時(shí) ,可以用角色實(shí)體來(lái)創(chuàng )建特定的時(shí)間關(guān)聯(lián)關(guān)系,從而可以實(shí)現自我文 檔化。
這里的含義不是讓 PERSON 實(shí)體帶有 Title 字段,而是說(shuō),為什么不用 PERSON 實(shí)體和 PERSON_TYPE實(shí)體來(lái)描述人員呢?比方說(shuō),當 John Smith, Engineer 提升為 John Smith, Director 乃至最后爬到John Smith, CIO 的高位,而所有你要做的不過(guò)是改變兩個(gè)表 PERSON 和 PERSON_TYPE之間關(guān)系的鍵值,同時(shí)增加一個(gè)日期/時(shí)間字段來(lái)知道變化是何時(shí)發(fā) 生的。這樣,你的 PERSON_TYPE 表就包含了所有 PERSON的可能類(lèi)型,比如 Associate、Engineer、Director 、CIO 或者 CEO 等。
還有個(gè)替代辦法就是改變 PERSON 記錄來(lái)反映新頭銜的變化,不過(guò)這樣一來(lái)在時(shí)間上無(wú)法跟蹤個(gè)人所處 位置的具體時(shí)間。
采用常用實(shí)體命名機構數據
組織數據的最簡(jiǎn)單辦法就是采用常用名字,比如:PERSON 、ORGANIZATION、ADDRESS 和 PHONE等等。當你把這些常用的一般名字組合起來(lái)或者創(chuàng )建特定的相應副實(shí) 體時(shí),你就得到了自己用的特殊版本。開(kāi)始的時(shí)候采用一般術(shù)語(yǔ)的主 要原因在于所有的具體用戶(hù)都能對抽象事物具體化。
有了這些抽象表示,你就可以在第 2 級標識中采用自己的特殊名稱(chēng),比如,PERSON 可能是Employee、Spouse、Patient、Client 、Customer、Vendor 或者 Teacher等。同樣的,ORGANIZATION 也可能是
MyCompany、MyDepartment、Competi tor、Hospital、Warehouse、Governm ent 等。最后ADDRESS 可以具體為 Site、Location、Home、Work、Client 、Vendor、Corporate 和FieldOffice 等。
采用一般抽象術(shù)語(yǔ)來(lái)標識"事物"的類(lèi)別可以讓你在關(guān)聯(lián)數據以滿(mǎn)足 業(yè)務(wù)要求方面獲得巨大的靈活性,同時(shí)這樣做還可以顯著(zhù)降低數據存 儲所需的冗余量。
用戶(hù)來(lái)自世界各地
在設計用到網(wǎng)絡(luò )或者具有其他國際特性的數據庫時(shí),一定要記住大多 數國家都有不同的字段格式,比如郵政編碼等,有些國家 ,比如新西蘭就沒(méi)有郵政編碼一說(shuō)。
數據重復需要采用分立的數據表
如果你發(fā)現自己在重復輸入數據,請創(chuàng )建新表和新的關(guān)系。
每個(gè)表中都應該添加的 3 個(gè)有用的字段
* dRecordCreationDate,在 VB 下默認是 Now(),而在 SQL Server 下默認為 GETDATE()
* sRecordCreator,在 SQL Server 下默認為 NOT NULL DEFAULT USER
* nRecordVersion,記錄的版本標記;有助于準確說(shuō)明 記錄中出現 null 數據或者丟失數據的原因
對地址和電話(huà)采用多個(gè)字段
描述街道地址就短短一行記錄是不夠的。Address _Line1、Address_Line2 和 Address_Line3
可以提供更大的靈活性。還有,電話(huà)號碼和郵件地址最好擁有自己的 數據表,其間具有自身的類(lèi)型和標記類(lèi)別。
過(guò)分標準化可要小心,這樣做可能會(huì )導致性能上出現問(wèn)題 。雖然地址和電話(huà)表分離通??梢赃_到最佳狀態(tài),但是如果需要經(jīng)常 訪(fǎng)問(wèn)這類(lèi)信息,或許在其父表中存放"首選"信息(比如
Customer 等)更為妥當些。非標準化和加速訪(fǎng)問(wèn)之間的妥協(xié)是有一定意義的。
使用多個(gè)名稱(chēng)字段
我覺(jué)得很吃驚,許多人在數據庫里就給 name留一個(gè)字段。我覺(jué)得只有剛入門(mén)的開(kāi)發(fā)人員才會(huì )這么做 ,但實(shí)際上網(wǎng)上這種做法非常普遍。我建議應該把姓氏和名字當作兩 個(gè)字段來(lái)處理,然后在查詢(xún)的時(shí)候再把他們組合起來(lái)。
我最常用的是在同一表中創(chuàng )建一個(gè)計算列[字段],通過(guò)它可以自動(dòng) 地連接標準化后的字段,這樣數據變動(dòng)的時(shí)候它也跟著(zhù)變。不過(guò) ,這樣做在采用建模軟件時(shí)得很機靈才行??傊?,采用連接字段的方 式可以有效的隔離用戶(hù)應用和開(kāi)發(fā)人員界面。
提防大小寫(xiě)混用的對象名和特殊字符過(guò)去最令我惱火的事情之一就是數據庫里有大小寫(xiě)混用的對象名 ,比如 CustomerData。這一問(wèn)題從 Access 到 Oracle數據庫都存在。我不喜歡采用這種大小寫(xiě)混用的對象命名方法 ,結果還不得不手工修改名字。想想看,這種數據庫 /應用程序能混到采用更強大數據庫的那一天嗎?采用全部大寫(xiě)而且 包含下劃符的名字具有更好的可讀性(CUSTOMER_DATA ),絕對不要在對象名的字符之間留空格。
小心保留詞
要保證你的字段名沒(méi)有和保留詞、數據庫系統或者常用訪(fǎng)問(wèn)方法沖突 ,比如,最近我編寫(xiě)的一個(gè) ODBC 連接程序里有個(gè)表,其中就用了 DESC作為說(shuō)明字段名。后果可想而知!DESC 是 DESCENDING 縮寫(xiě)后的保留詞。表里的一個(gè) SELECT *語(yǔ)句倒是能用,但我得到的卻是一大堆毫無(wú)用處的信息。
保持字段名和類(lèi)型的一致性
在命名字段并為其指定數據類(lèi)型的時(shí)候一定要保證一致性 。假如字段在某個(gè)表中叫做"agreement_number" ,你就別在另一個(gè)表里把名字改成"ref1"。假如數據類(lèi)型在一 個(gè)表里是整數,那在另一個(gè)表里可就別變成字符型了。記住 ,你干完自己的活了,其他人還要用你的數據庫呢。
仔細選擇數字類(lèi)型
在 SQL 中使用 smallint 和 tinyint 類(lèi)型要特別小心,比如,假如你想看看月銷(xiāo)售總額,你的總額字段類(lèi) 型是smallint,那么,如果總額超過(guò)了 $32,767 你就不能進(jìn)行計算操作了。
刪除標記
在表中包含一個(gè)"刪除標記"字段,這樣就可以把行標記為刪除 。在關(guān)系數據庫里不要單獨刪除某一行;最好采用清除數據程序而且 要仔細維護索引整體性。
避免使用觸發(fā)器
觸發(fā)器的功能通??梢杂闷渌绞綄?shí)現。在調試程序時(shí)觸發(fā)器可能成 為干擾。假如你確實(shí)需要采用觸發(fā)器,你最好集中對它文檔化。
包含版本機制建議你在數據庫中引入版本控制機制來(lái)確定使用中的數據庫的版本 。無(wú)論如何你都要實(shí)現這一要求。時(shí)間一長(cháng),用戶(hù)的需求總是會(huì )改變 的。最終可能會(huì )要求修改數據庫結構。雖然你可以通過(guò)檢查新字段或 者索引來(lái)確定數據庫結構的版本,但我發(fā)現把版本信息直接存放到數 據庫中不更為方便嗎?
給文本字段留足余量
ID 類(lèi)型的文本字段,比如客戶(hù) ID或定單號等等都應該設置得比一般想象更大,因為時(shí)間不長(cháng)你多半就 會(huì )因為要添加額外的字符而難堪不已。比方說(shuō),假設你的客戶(hù) ID 為 10位數長(cháng)。那你應該把數據庫表字段的長(cháng)度設為 12 或者 13 個(gè)字符長(cháng)。這算浪費空間嗎?是有一點(diǎn),但也沒(méi)你想象的那么多 :一個(gè)字段加長(cháng) 3個(gè)字符在有 1 百萬(wàn)條記錄,再加上一點(diǎn)索引的情況下才不過(guò)讓整個(gè)數據庫多占據 3MB的空間。但這額外占據的空間卻無(wú)需將來(lái)重構整個(gè)數據庫就可以實(shí)現 數據庫規模的增長(cháng)了。身份證的號碼從 15 位變成 18
位就是最好和最慘痛的例子。
列[字段]命名技巧
我們發(fā)現,假如你給每個(gè)表的列[字段]名都采用統一的前綴 ,那么在編寫(xiě) SQL表達式的時(shí)候會(huì )得到大大的簡(jiǎn)化。這樣做也確實(shí)有缺點(diǎn) ,比如破壞了自動(dòng)表連接工具的作用,后者把公共列[字段 ]名同某些數據庫聯(lián)系起來(lái),不過(guò)就連這些工具有時(shí)不也連接錯誤嘛 。舉個(gè)簡(jiǎn)單的例子,假設有兩個(gè)表:
Customer 和 Order。Customer 表的前綴是cu_,所以該表內的子段名如下:cu_name_id、cu _surname、cu_initials 和cu_address 等。Order表的前綴是 or_,所以子段名是:
or_order_id、or_cust_name_id、or _quantity 和 or_denoscription 等。
這樣從數據庫中選出全部數據的 SQL 語(yǔ)句可以寫(xiě)成如下所示:
Select * From Customer, Order Where cu_surname = "MYNAME" ;
and cu_name_id = or_cust_name_id and or_quantity = 1
在沒(méi)有這些前綴的情況下則寫(xiě)成這個(gè)樣子(用別名來(lái)區分):
Select * From Customer, Order Where Customer.surname = "MYNAME" ;
and Customer.name_id = Order.cust_name_id and Order.quantity = 1
第 1 個(gè) SQL 語(yǔ)句沒(méi)少鍵入多少字符。但如果查詢(xún)涉及到 5 個(gè)表乃至更多的列[字段]你就知道這個(gè)技巧多有用了。
第 3 部分 - 選擇鍵和索引
數據采掘要預先計劃
我所在的某一客戶(hù)部門(mén)一度要處理 8萬(wàn)多份聯(lián)系方式,同時(shí)填寫(xiě)每個(gè)客戶(hù)的必要數據(這絕對不是小活) 。我從中還要確定出一組客戶(hù)作為市場(chǎng)目標。當我從最開(kāi)始設計表和 字段的時(shí)候,我試圖不在主索引里增加太多的字段以便加快數據庫的 運行速度。然后我意識到特定的組查詢(xún)和信息采掘既不準確速度也不 快。結果只好在主索引中重建而且合并了數據字段。我發(fā)現有一個(gè)指 示計劃相當關(guān)鍵——當我想創(chuàng )建系統類(lèi)型查找時(shí)為什么要采用號碼作 為主索引字段呢?我可以用傳真號碼進(jìn)行檢索,但是它幾乎就象系統 類(lèi)型一樣對我來(lái)說(shuō)并不重要。采用后者作為主字段,數據庫更新后重 新索引和檢索就快多了。
可操作數據倉庫(ODS)和數據倉庫(DW)這兩種環(huán)境下的數據 索引是有差別的。在 DW環(huán)境下,你要考慮銷(xiāo)售部門(mén)是如何組織銷(xiāo)售活動(dòng)的。他們并不是數據 庫管理員,但是他們確定表內的鍵信息。這里設計人員或者數據庫工 作人員應該分析數據庫結構從而確定出性能和正確輸出之間的最佳條 件。
使用系統生成的主鍵
這類(lèi)同技巧 1,但我覺(jué)得有必要在這里重復提醒大家。假如你總是在設計數據庫 的時(shí)候采用系統生成的鍵作為主鍵,那么你實(shí)際控制了數據庫的索引 完整性。這樣,數據庫和非人工機制就有效地控制了對存儲數據中每 一行的訪(fǎng)問(wèn)。
采用系統生成鍵作為主鍵還有一個(gè)優(yōu)點(diǎn):當你擁有一致的鍵結構時(shí) ,找到邏輯缺陷很容易。
分解字段用于索引
為了分離命名字段和包含字段以支持用戶(hù)定義的報表 ,請考慮分解其他字段(甚至主鍵)為其組成要素以便用戶(hù)可以對其 進(jìn)行索引。索引將加快 SQL和報表生成器腳本的執行速度。比方說(shuō),我通常在必須使用 SQL LIKE 表達式的情況下創(chuàng )建報表,因為 case number 字段無(wú)法分解為year、serial number、case type 和 defendant code等要素。性能也會(huì )變壞。假如年度和類(lèi)型字段可以分解為索引字段那 么這些報表運行起來(lái)就會(huì )快多了。
鍵設計 4 原則
* 為關(guān)聯(lián)字段創(chuàng )建外鍵。
* 所有的鍵都必須唯一。
* 避免使用復合鍵。
* 外鍵總是關(guān)聯(lián)唯一的鍵字段。
別忘了索引
索引是從數據庫中獲取數據的最高效方式之一。95%的數據庫性能問(wèn)題都可以采用索引技術(shù)得到解決。作為一條規則 ,我通常對邏輯主鍵使用唯一的成組索引,對系統鍵(作為存儲過(guò)程 )采用唯一的非成組索引,對任何外鍵列[字段]采用非成組索引 。不過(guò),索引就象是鹽,太多了菜就咸了。你得考慮數據庫的空間有 多大,表如何進(jìn)行訪(fǎng)問(wèn),還有這些訪(fǎng)問(wèn)是否主要用作讀寫(xiě)。
大多數數據庫都索引自動(dòng)創(chuàng )建的主鍵字段,但是可別忘了索引外鍵 ,它們也是經(jīng)常使用的鍵,比如運行查詢(xún)顯示主表和所有關(guān)聯(lián)表的某 條記錄就用得上。還有,不要索引memo/note 字段,不要索引大型字段(有很多字符),這樣作會(huì )讓索引占用太多 的存儲空間。
不要索引常用的小型表不要為小型數據表設置任何鍵,假如它們經(jīng)常有插入和刪除操作就更 別這樣作了。對這些插入和刪除操作的索引維護可能比掃描表空間消 耗更多的時(shí)間。
不要把社會(huì )保障號碼(SSN)或身份證號碼(ID)選作鍵。
永遠都不要使用 SSN 或 ID 作為數據庫的鍵。除了隱私原因以外,須知政府越來(lái)越趨向于不準許 把 SSN 或 ID
用作除收入相關(guān)以外的其他目的,SSN 或 ID需要手工輸入。永遠不要使用手工輸入的鍵作為主鍵 ,因為一旦你輸入錯誤,你唯一能做的就是刪除整個(gè)記錄然后從頭開(kāi) 始。
我在破解他人的程序時(shí)候,我看到很多人把 SSN 或 ID還曾被用做系列號,當然盡管這么做是非法的。而且人們也都知道這 是非法的,但他們已經(jīng)習慣了。后來(lái),隨著(zhù)盜取身份犯罪案件的增加 ,我現在的同行正痛苦地從一大攤子數據中把 SSN 或 ID 刪除。
不要用用戶(hù)的鍵
在確定采用什么字段作為表的鍵的時(shí)候,可一定要小心用戶(hù)將要編輯 的字段。通常的情況下不要選擇用戶(hù)可編輯的字段作為鍵 。這樣做會(huì )迫使你采取以下兩個(gè)措施:
* 在創(chuàng )建記錄之后對用戶(hù)編輯字段的行為施加限制。假如你這么做了 ,你可能會(huì )發(fā)現你的應用程序在商務(wù)需求突然發(fā)生變化 ,而用戶(hù)需要編輯那些不可編輯的字段時(shí)缺乏足夠的靈活性 。當用戶(hù)在輸入數據之后直到保存記錄才發(fā)現系統出了問(wèn)題他們該怎 么想?刪除重建?假如記錄不可重建是否讓用戶(hù)走開(kāi)?
* 提出一些檢測和糾正鍵沖突的方法。通常,費點(diǎn)精力也就搞定了 ,但是從性能上來(lái)看這樣做的代價(jià)就比較大了。還有 ,鍵的糾正可能會(huì )迫使你突破你的數據和商業(yè)/用戶(hù)界面層之間的隔 離。
所以還是重提一句老話(huà):你的設計要適應用戶(hù)而不是讓用戶(hù)來(lái)適應你 的設計。
不讓主鍵具有可更新性的原因是在關(guān)系模式下,主鍵實(shí)現了不同表之 間的關(guān)聯(lián)。比如,Customer 表有一個(gè)主鍵CustomerID,而客戶(hù)的定單則存放在另一個(gè)表里 。Order 表的主鍵可能是 OrderNo 或者 OrderNo、CustomerID和日期的組合。不管你選擇哪種鍵設置,你都需要在 Order 表中存放 CustomerID 來(lái)保證你可以給下定單的用戶(hù)找到其定單記錄。
假如你在 Customer 表里修改了 CustomerID,那么你必須找出 Order表中的所有相關(guān)記錄對其進(jìn)行修改。否則,有些定單就會(huì )不屬于任何 客戶(hù)——數據庫的完整性就算完蛋了。
如果索引完整性規則施加到表一級,那么在不編寫(xiě)大量代碼和附加刪 除記錄的情況下幾乎不可能改變某一條記錄的鍵和數據庫內所有關(guān)聯(lián) 的記錄。而這一過(guò)程往往錯誤叢生所以應該盡量避免。
可選鍵(候選鍵)有時(shí)可做主鍵
記住,查詢(xún)數據的不是機器而是人。
假如你有可選鍵,你可能進(jìn)一步把它用做主鍵。那樣的話(huà) ,你就擁有了建立強大索引的能力。這樣可以阻止使用數據庫的人不 得不連接數據庫從而恰當的過(guò)濾數據。在嚴格控制域表的數據庫上 ,這種負載是比較醒目的。如果可選鍵真正有用,那就是達到了主鍵 的水準。
我的看法是,假如你有可選鍵,比如國家表內的state_code,你不要在現有不能變動(dòng)的唯一鍵上創(chuàng )建后續 的鍵。你要做的無(wú)非是創(chuàng )建毫無(wú)價(jià)值的數據。如你因為過(guò)度使用表的 后續鍵[別名]建立這種表的關(guān)聯(lián),操作負載真得需要考慮一下了。
別忘了外鍵
大多數數據庫索引自動(dòng)創(chuàng )建的主鍵字段。但別忘了索引外鍵字段 ,它們在你想查詢(xún)主表中的記錄及其關(guān)聯(lián)記錄時(shí)每次都會(huì )用到。還有 ,不要索引memo/notes 字段而且不要索引大型文本字段(許多字符),這樣做會(huì )讓你的索引 占據大量的數據庫空間。
第 4 部分 - 保證數據的完整性
用約束而非商務(wù)規則強制數據完整性
如果你按照商務(wù)規則來(lái)處理需求,那么你應當檢查商務(wù)層次 /用戶(hù)界面:如果商務(wù)規則以后發(fā)生變化,那么只需要進(jìn)行更新即可 。假如需求源于維護數據完整性的需要,那么在數據庫層面上需要施 加限制條件。如果你在數據層確實(shí)采用了約束,你要保證有辦法把更 新不能通過(guò)約束檢查的原因采用用戶(hù)理解的語(yǔ)言通知用戶(hù)界面 。除非你的字段命名很冗長(cháng),否則字段名本身還不夠。
只要有可能,請采用數據庫系統實(shí)現數據的完整性。這不但包括通過(guò) 標準化實(shí)現的完整性而且還包括數據的功能性。在寫(xiě)數據的時(shí)候還可 以增加觸發(fā)器來(lái)保證數據的正確性。不要依賴(lài)于商務(wù)層保證數據完整 性;它不能保證表之間(外鍵)的完整性所以不能強加于其他完整性 規則之上。
分布式數據系統
對分布式系統而言,在你決定是否在各個(gè)站點(diǎn)復制所有數據還是把數 據保存在一個(gè)地方之前應該估計一下未來(lái) 5 年或者 10
年的數據量。當你把數據傳送到其他站點(diǎn)的時(shí)候,最好在數據庫字段 中設置一些標記。在目的站點(diǎn)收到你的數據之后更新你的標記 。為了進(jìn)行這種數據傳輸,請寫(xiě)下你自己的批處理或者調度程序以特 定時(shí)間間隔運行而不要讓用戶(hù)在每天的工作后傳輸數據 。本地拷貝你的維護數據,比如計算常數和利息率等 ,設置版本號保證數據在每個(gè)站點(diǎn)都完全一致。
強制指示完整性(參照完整性?)
沒(méi)有好辦法能在有害數據進(jìn)入數據庫之后消除它,所以你應該在它進(jìn) 入數據庫之前將其剔除。激活數據庫系統的指示完整性特性 。這樣可以保持數據的清潔而能迫使開(kāi)發(fā)人員投入更多的時(shí)間處理錯誤條件。
關(guān)系
如果兩個(gè)實(shí)體之間存在多對一關(guān)系,而且還有可能轉化為多對多關(guān)系 ,那么你最好一開(kāi)始就設置成多對多關(guān)系。從現有的多對一關(guān)系轉變 為多對多關(guān)系比一開(kāi)始就是多對多關(guān)系要難得多。
采用視圖
為了在你的數據庫和你的應用程序代碼之間提供另一層抽象 ,你可以為你的應用程序建立專(zhuān)門(mén)的視圖而不必非要應用程序直接訪(fǎng) 問(wèn)數據表。這樣做還等于在處理數據庫變更時(shí)給你提供了更多的自由 。
給數據保有和恢復制定計劃
考慮數據保有策略并包含在設計過(guò)程中,預先設計你的數據恢復過(guò)程 。采用可以發(fā)布給用戶(hù)/開(kāi)發(fā)人員的數據字典實(shí)現方便的數據識別同 時(shí)保證對數據源文檔化。編寫(xiě)在線(xiàn)更新來(lái)"更新查詢(xún) "供以后萬(wàn)一數據丟失可以重新處理更新。
用存儲過(guò)程讓系統做重活解決了許多麻煩來(lái)產(chǎn)生一個(gè)具有高度完整性的數據庫解決方案之后 ,我決定封裝一些關(guān)聯(lián)表的功能組,提供一整套常規的存儲過(guò)程來(lái)訪(fǎng) 問(wèn)各組以便加快速度和簡(jiǎn)化客戶(hù)程序代碼的開(kāi)發(fā)。數據庫不只是一個(gè) 存放數據的地方,它也是簡(jiǎn)化編碼之地。
使用查找
控制數據完整性的最佳方式就是限制用戶(hù)的選擇。只要有可能都應該 提供給用戶(hù)一個(gè)清晰的價(jià)值列表供其選擇。這樣將減少鍵入代碼的錯 誤和誤解同時(shí)提供數據的一致性。某些公共數據特別適合查找 :國家代碼、狀態(tài)代碼等。
第 5 部分 - 各種小技巧
文檔、文檔、文檔
對所有的快捷方式、命名規范、限制和函數都要編制文檔。
采用給表、列[字段]、觸發(fā)器等加注釋的數據庫工具。是的 ,這有點(diǎn)費事,但從長(cháng)遠來(lái)看,這樣做對開(kāi)發(fā)、支持和跟蹤修改非常 有用。
取決于你使用的數據庫系統,可能有一些軟件會(huì )給你一些供你很快上 手的文檔。你可能希望先開(kāi)始在說(shuō),然后獲得越來(lái)越多的細節 ?;蛘吣憧赡芟M芷谛缘念A排,在輸入新數據同時(shí)隨著(zhù)你的進(jìn)展對 每一部分細節化。不管你選擇哪種方式,總要對你的數據庫文檔化 ,或者在數據庫自身的內部或者單獨建立文檔。這樣 ,當你過(guò)了一年多時(shí)間后再回過(guò)頭來(lái)做第2 個(gè)版本,你犯錯的機會(huì )將大大減少。
使用常用英語(yǔ)(或者其他任何語(yǔ)言)而不要使用編碼
為什么我們經(jīng)常采用編碼(比如 9935A 可能是‘青島啤酒‘的供應代碼,4XF788-Q可能是帳目編碼)?理由很多。但是用戶(hù)通常都用英語(yǔ)進(jìn)行思考而不 是編碼。工作 5 年的會(huì )計或許知道 4XF788-Q是什么東西,但新來(lái)的可就不一定了。在創(chuàng )建下拉菜單、列表 、報表時(shí)最好按照英語(yǔ)名排序。假如你需要編碼,那你可以在編碼旁 附上用戶(hù)知道的英語(yǔ)。
保存常用信息
讓一個(gè)表專(zhuān)門(mén)存放一般數據庫信息非常有用。我常在這個(gè)表里存放數 據庫當前版本、最近檢查/修復(對FoxPro)、關(guān)聯(lián)設計文檔的名稱(chēng)、客戶(hù)等信息 。這樣可以實(shí)現一種簡(jiǎn)單機制跟蹤數據庫,當客戶(hù)抱怨他們的數據庫 沒(méi)有達到希望的要求而與你聯(lián)系時(shí),這樣做對非客戶(hù)機 /服務(wù)器環(huán)境特別有用。
測試、測試、反復測試
建立或者修訂數據庫之后,必須用用戶(hù)新輸入的數據測試數據字段 。最重要的是,讓用戶(hù)進(jìn)行測試并且同用戶(hù)一道保證你選擇的數據類(lèi) 型滿(mǎn)足商業(yè)要求。測試需要在把新數據庫投入實(shí)際服務(wù)之前完成。
檢查設計
在開(kāi)發(fā)期間檢查數據庫設計的常用技術(shù)是通過(guò)其所支持的應用程序原 型檢查數據庫。換句話(huà)說(shuō),針對每一種最終表達數據的原型應用 ,保證你檢查了數據模型并且查看如何取出數據。
Microsoft Visual FoxPro 設計技巧
對復雜的 Microsoft Visual FoxPro數據庫應用程序而言,可以把所有的主表放在一個(gè)數據庫容器文件里 ,然后增加其他數據庫表文件和裝載同原有數據庫有關(guān)的特殊文件 。根據需要用這些文件連接到主文件中的主表。比如數據輸入 、數據索引、統計分析、向管理層或者政府部門(mén)提供報表以及各類(lèi)只 讀查詢(xún)等。這一措施簡(jiǎn)化了用戶(hù)和組權限的分配,而且有利于應用程 序函數(存儲過(guò)程)的分組和劃分,從而在程序必須修改的時(shí)候易于 管理。
聯(lián)系客服