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