數(shù)據(jù)庫經(jīng)驗談 二(引)
2024-07-21 02:05:45
供稿:網(wǎng)友
 
第 2 部分 - 設(shè)計表和字段
1. 檢查各種變化
我在設(shè)計數(shù)據(jù)庫的時候會考慮到哪些數(shù)據(jù)字段將來可能會發(fā)生變更。比方說,姓氏就是如此(注意是西方人的姓氏,比如女性結(jié)婚后從夫姓等)。所以,在建立系統(tǒng)存儲客戶信息時,我傾向于在單獨的一個數(shù)據(jù)表里存儲姓氏字段,而且還附加起始日和終止日等字段,這樣就可以跟蹤這一數(shù)據(jù)條目的變化。
2. 采用有意義的字段名
有一回我參加開發(fā)過一個項目,其中有從其他程序員那里繼承的程序,那個程序員喜歡用屏幕上顯示數(shù)據(jù)指示用語命名字段,這也不賴,但不幸的是,她還喜歡用一些奇怪的命名法,其命名采用了匈牙利命名和控制序號的組合形式,比如 cbo1、txt2、txt2_b 等等。
除非你在使用只面向你的縮寫字段名的系統(tǒng),否則請盡可能地把字段描述的清楚些。當然,也別做過頭了,比如 customer_shipping_address_street_line_1,雖然很富有說明性,但沒人愿意鍵入這么長的名字,具體尺度就在你的把握中。
3. 采用前綴命名
如果多個表里有好多同一類型的字段(比如 firstname),你不妨用特定表的前綴(比如 cuslastname)來幫助你標識字段。
時效性數(shù)據(jù)應(yīng)包括“最近更新日期/時間”字段。時間標記對查找數(shù)據(jù)問題的原因、按日期重新處理/重載數(shù)據(jù)和清除舊數(shù)據(jù)特別有用。
5. 標準化和數(shù)據(jù)驅(qū)動
數(shù)據(jù)的標準化不僅方便了自己而且也方便了其他人。比方說,假如你的用戶界面要訪問外部數(shù)據(jù)源(文件、xml 文檔、其他數(shù)據(jù)庫等),你不妨把相應(yīng)的連接和路徑信息存儲在用戶界面支持表里。還有,如果用戶界面執(zhí)行工作流之類的任務(wù)(發(fā)送郵件、打印信箋、修改記錄狀態(tài)等),那么產(chǎn)生工作流的數(shù)據(jù)也可以存放在數(shù)據(jù)庫里。預先安排總需要付出努力,但如果這些過程采用數(shù)據(jù)驅(qū)動而非硬編碼的方式,那么策略變更和維護都會方便得多。事實上,如果過程是數(shù)據(jù)驅(qū)動的,你就可以把相當大的責任推給用戶,由用戶來維護自己的工作流過程。
6. 標準化不能過頭
對那些不熟悉標準化一詞(normalization)的人而言,標準化可以保證表內(nèi)的字段都是最基礎(chǔ)的要素,而這一措施有助于消除數(shù)據(jù)庫中的數(shù)據(jù)冗余。標準化有好幾種形式,但 third normal form(3nf)通常被認為在性能、擴展性和數(shù)據(jù)完整性方面達到了最好平衡。簡單來說,3nf 規(guī)定:
* 表內(nèi)的每一個值都只能被表達一次。
* 表內(nèi)的每一行都應(yīng)該被唯一的標識(有唯一鍵)。
* 表內(nèi)不應(yīng)該存儲依賴于其他鍵的非鍵信息。
遵守 3nf 標準的數(shù)據(jù)庫具有以下特點:有一組表專門存放通過鍵連接起來的關(guān)聯(lián)數(shù)據(jù)。比方說,某個存放客戶及其有關(guān)定單的 3nf 數(shù)據(jù)庫就可能有兩個表:customer 和 order。order 表不包含定單關(guān)聯(lián)客戶的任何信息,但表內(nèi)會存放一個鍵值,該鍵指向 customer 表里包含該客戶信息的那一行。
更高層次的標準化也有,但更標準是否就一定更好呢?答案是不一定。事實上,對某些項目來說,甚至就連 3nf 都可能給數(shù)據(jù)庫引入太高的復雜性。
為了效率的緣故,對表不進行標準化有時也是必要的,這樣的例子很多。曾經(jīng)有個開發(fā)餐飲分析軟件的活就是用非標準化表把查詢時間從平均 40 秒降低到了兩秒左右。雖然我不得不這么做,但我絕不把數(shù)據(jù)表的非標準化當作當然的設(shè)計理念。而具體的操作不過是一種派生。所以如果表出了問題重新產(chǎn)生非標準化的表是完全可能的。
7. microsoft visual foxpro 報表技巧
如果你正在使用 microsoft visual foxpro,你可以用對用戶友好的字段名來代替編號的名稱:比如用 customer name 代替 txtcnam。這樣,當你用向?qū)С绦?[wizards,臺灣人稱為‘精靈’] 創(chuàng)建表單和報表時,其名字會讓那些不是程序員的人更容易閱讀。
8. 不活躍或者不采用的指示符
增加一個字段表示所在記錄是否在業(yè)務(wù)中不再活躍挺有用的。不管是客戶、員工還是其他什么人,這樣做都能有助于再運行查詢的時候過濾活躍或者不活躍狀態(tài)。同時還消除了新用戶在采用數(shù)據(jù)時所面臨的一些問題,比如,某些記錄可能不再為他們所用,再刪除的時候可以起到一定的防范作用。
9. 使用角色實體定義屬于某類別的列[字段]
在需要對屬于特定類別或者具有特定角色的事物做定義時,可以用角色實體來創(chuàng)建特定的時間關(guān)聯(lián)關(guān)系,從而可以實現(xiàn)自我文檔化。
這里的含義不是讓 person 實體帶有 title 字段,而是說,為什么不用 person 實體和 person_type 實體來描述人員呢?比方說,當 john smith, engineer 提升為 john smith, director 乃至最后爬到 john smith, cio 的高位,而所有你要做的不過是改變兩個表 person 和 person_type 之間關(guān)系的鍵值,同時增加一個日期/時間字段來知道變化是何時發(fā)生的。這樣,你的 person_type 表就包含了所有 person 的可能類型,比如 associate、engineer、director、cio 或者 ceo 等。
還有個替代辦法就是改變 person 記錄來反映新頭銜的變化,不過這樣一來在時間上無法跟蹤個人所處位置的具體時間。
10. 采用常用實體命名機構(gòu)數(shù)據(jù)
組織數(shù)據(jù)的最簡單辦法就是采用常用名字,比如:person、organization、address 和 phone 等等。當你把這些常用的一般名字組合起來或者創(chuàng)建特定的相應(yīng)副實體時,你就得到了自己用的特殊版本。開始的時候采用一般術(shù)語的主要原因在于所有的具體用戶都能對抽象事物具體化。
有了這些抽象表示,你就可以在第 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 等。
采用一般抽象術(shù)語來標識“事物”的類別可以讓你在關(guān)聯(lián)數(shù)據(jù)以滿足業(yè)務(wù)要求方面獲得巨大的靈活性,同時這樣做還可以顯著降低數(shù)據(jù)存儲所需的冗余量。
11. 用戶來自世界各地
在設(shè)計用到網(wǎng)絡(luò)或者具有其他國際特性的數(shù)據(jù)庫時,一定要記住大多數(shù)國家都有不同的字段格式,比如郵政編碼等,有些國家,比如新西蘭就沒有郵政編碼一說。
12. 數(shù)據(jù)重復需要采用分立的數(shù)據(jù)表
如果你發(fā)現(xiàn)自己在重復輸入數(shù)據(jù),請創(chuàng)建新表和新的關(guān)系。
13. 每個表中都應(yīng)該添加的 3 個有用的字段
* drecordcreationdate,在 vb 下默認是 now(),而在 sql server 下默認為 getdate()
* srecordcreator,在 sql server 下默認為 not null default user
* nrecordversion,記錄的版本標記;有助于準確說明記錄中出現(xiàn) null 數(shù)據(jù)或者丟失數(shù)據(jù)的原因
14. 對地址和電話采用多個字段
描述街道地址就短短一行記錄是不夠的。address_line1、address_line2 和 address_line3 可以提供更大的靈活性。還有,電話號碼和郵件地址最好擁有自己的數(shù)據(jù)表,其間具有自身的類型和標記類別。
過分標準化可要小心,這樣做可能會導致性能上出現(xiàn)問題。雖然地址和電話表分離通常可以達到最佳狀態(tài),但是如果需要經(jīng)常訪問這類信息,或許在其父表中存放“首選”信息(比如 customer 等)更為妥當些。非標準化和加速訪問之間的妥協(xié)是有一定意義的。
15. 使用多個名稱字段
我覺得很吃驚,許多人在數(shù)據(jù)庫里就給 name 留一個字段。我覺得只有剛?cè)腴T的開發(fā)人員才會這么做,但實際上網(wǎng)上這種做法非常普遍。我建議應(yīng)該把姓氏和名字當作兩個字段來處理,然后在查詢的時候再把他們組合起來。
我最常用的是在同一表中創(chuàng)建一個計算列[字段],通過它可以自動地連接標準化后的字段,這樣數(shù)據(jù)變動的時候它也跟著變。不過,這樣做在采用建模軟件時得很機靈才行。總之,采用連接字段的方式可以有效的隔離用戶應(yīng)用和開發(fā)人員界面。
6. 提防大小寫混用的對象名和特殊字符
過去最令我惱火的事情之一就是數(shù)據(jù)庫里有大小寫混用的對象名,比如 customerdata。這一問題從 access 到 oracle 數(shù)據(jù)庫都存在。我不喜歡采用這種大小寫混用的對象命名方法,結(jié)果還不得不手工修改名字。想想看,這種數(shù)據(jù)庫/應(yīng)用程序能混到采用更強大數(shù)據(jù)庫的那一天嗎?采用全部大寫而且包含下劃符的名字具有更好的可讀性(customer_data),絕對不要在對象名的字符之間留空格。
17. 小心保留詞
要保證你的字段名沒有和保留詞、數(shù)據(jù)庫系統(tǒng)或者常用訪問方法沖突,比如,最近我編寫的一個 odbc 連接程序里有個表,其中就用了 desc 作為說明字段名。后果可想而知!desc 是 descending 縮寫后的保留詞。表里的一個 select * 語句倒是能用,但我得到的卻是一大堆毫無用處的信息。
18. 保持字段名和類型的一致性
在命名字段并為其指定數(shù)據(jù)類型的時候一定要保證一致性。假如字段在某個表中叫做“agreement_number”,你就別在另一個表里把名字改成“ref1”。假如數(shù)據(jù)類型在一個表里是整數(shù),那在另一個表里可就別變成字符型了。記住,你干完自己的活了,其他人還要用你的數(shù)據(jù)庫呢。
19. 仔細選擇數(shù)字類型
在 sql 中使用 smallint 和 tinyint 類型要特別小心,比如,假如你想看看月銷售總額,你的總額字段類型是 smallint,那么,如果總額超過了 $32,767 你就不能進行計算操作了。
20. 刪除標記
在表中包含一個“刪除標記”字段,這樣就可以把行標記為刪除。在關(guān)系數(shù)據(jù)庫里不要單獨刪除某一行;最好采用清除數(shù)據(jù)程序而且要仔細維護索引整體性。
21. 避免使用觸發(fā)器
觸發(fā)器的功能通常可以用其他方式實現(xiàn)。在調(diào)試程序時觸發(fā)器可能成為干擾。假如你確實需要采用觸發(fā)器,你最好集中對它文檔化。
22. 包含版本機制
建議你在數(shù)據(jù)庫中引入版本控制機制來確定使用中的數(shù)據(jù)庫的版本。無論如何你都要實現(xiàn)這一要求。時間一長,用戶的需求總是會改變的。最終可能會要求修改數(shù)據(jù)庫結(jié)構(gòu)。雖然你可以通過檢查新字段或者索引來確定數(shù)據(jù)庫結(jié)構(gòu)的版本,但我發(fā)現(xiàn)把版本信息直接存放到數(shù)據(jù)庫中不更為方便嗎?。
23. 給文本字段留足余量
id 類型的文本字段,比如客戶 id 或定單號等等都應(yīng)該設(shè)置得比一般想象更大,因為時間不長你多半就會因為要添加額外的字符而難堪不已。比方說,假設(shè)你的客戶 id 為 10 位數(shù)長。那你應(yīng)該把數(shù)據(jù)庫表字段的長度設(shè)為 12 或者 13 個字符長。這算浪費空間嗎?是有一點,但也沒你想象的那么多:一個字段加長 3 個字符在有 1 百萬條記錄,再加上一點索引的情況下才不過讓整個數(shù)據(jù)庫多占據(jù) 3mb 的空間。但這額外占據(jù)的空間卻無需將來重構(gòu)整個數(shù)據(jù)庫就可以實現(xiàn)數(shù)據(jù)庫規(guī)模的增長了。身份證的號碼從 15 位變成 18 位就是最好和最慘痛的例子。
24. 列[字段]命名技巧
我們發(fā)現(xiàn),假如你給每個表的列[字段]名都采用統(tǒng)一的前綴,那么在編寫 sql 表達式的時候會得到大大的簡化。這樣做也確實有缺點,比如破壞了自動表連接工具的作用,后者把公共列[字段]名同某些數(shù)據(jù)庫聯(lián)系起來,不過就連這些工具有時不也連接錯誤嘛。舉個簡單的例子,假設(shè)有兩個表:
customer 和 order。customer 表的前綴是 cu_,所以該表內(nèi)的子段名如下:cu_name_id、cu_surname、cu_initials 和cu_address 等。order 表的前綴是 or_,所以子段名是:
or_order_id、or_cust_name_id、or_quantity 和 or_description 等。
這樣從數(shù)據(jù)庫中選出全部數(shù)據(jù)的 sql 語句可以寫成如下所示:
select * from customer, order where cu_surname = "myname" ;
and cu_name_id = or_cust_name_id and or_quantity = 1
在沒有這些前綴的情況下則寫成這個樣子(用別名來區(qū)分):
select * from customer, order where customer.surname = "myname" ;
and customer.name_id = order.cust_name_id and order.quantity = 1
第 1 個 sql 語句沒少鍵入多少字符。但如果查詢涉及到 5 個表乃至更多的列[字段]你就知道這個技巧多有用了。