轉(zhuǎn)自:http://blog.csdn.net/ccat/archive/2009/05/14/4180765.aspx
    幾乎每一個(gè)新人在初學(xué)關(guān)系型數(shù)據(jù)庫設(shè)計(jì)的時(shí)候,都會(huì)接觸到關(guān)系范式。但是,我還是見到了大量很離譜的設(shè)計(jì)。客觀的說,背下關(guān)系范式,離一個(gè)合格的數(shù)據(jù)庫設(shè) 計(jì)師還差很遠(yuǎn)。設(shè)計(jì)工作總是在理想與現(xiàn)實(shí)之,規(guī)范與工藝之間妥協(xié)。建筑如是,造船如是,操作系統(tǒng)設(shè)計(jì)如是,數(shù)據(jù)庫設(shè)計(jì)亦如是。
     是的,你記得范式,你還記得反范式建議。你知道范式減少冗余,提高一致性;你還知道反范式可以方便編程。不幸的是,最終的結(jié)果總是遵守范式的做法使自己的應(yīng)用層代碼混亂,而反范式的企圖使得數(shù)據(jù)庫也陷入混亂。
這是誰的錯(cuò)?
     不必太自責(zé),設(shè)計(jì)工作是一個(gè)經(jīng)驗(yàn)的積累過程。沒有人天生就會(huì)做設(shè)計(jì)。天才與勤奮,是乘法關(guān)系。并不是你笨,只是天才對面的那個(gè)系數(shù)還不夠大而已。
     以下的一些經(jīng)驗(yàn),或許在你讀完關(guān)系范式以后,可以抽空看一看 。世上沒有魔法,讀完這篇文章,并不會(huì)立即讓你擁有多年設(shè)計(jì)經(jīng)驗(yàn)。但是,這些在設(shè)計(jì)工作中積累的經(jīng)驗(yàn)教訓(xùn),應(yīng)該可以幫助你少走一些彎路。

關(guān)于范式

     關(guān)系范式并不邪惡,也不要把它想得太神秘,如果書本上的定義不能讓你很快理解,不妨試著回答以下的問題:
     字段還可以再分嗎?分成兩個(gè)或更多的字段以后,還能不能表達(dá)完整的含義?
     字段的值是不是有限的幾個(gè)離散的狀態(tài)?
     兩個(gè)或若干個(gè)字段,能不能提取出來建立為一個(gè)數(shù)據(jù)字典?
     如果表中某個(gè)字段依賴其他表,被依賴的字段是不是唯一的(最好是主鍵)?
     查詢中是否會(huì)出現(xiàn)超過兩個(gè)表的Join?

將數(shù)據(jù)庫設(shè)計(jì)與系統(tǒng)設(shè)計(jì)結(jié)合起來

     數(shù)據(jù)庫設(shè)計(jì)并不是一個(gè)孤立的過程,整個(gè)軟件生命期中,各方面的工作應(yīng)該有機(jī)結(jié)合。這方面我覺得ACCP過去的教材講得還不錯(cuò),至少思路是對的:
     在做需求分析的時(shí)候,做Use Case。 此時(shí)可以分析出應(yīng)用層的功能接口,對于數(shù)據(jù)庫的實(shí)體分類可以有一個(gè)大概的劃定。例如,這個(gè)項(xiàng)目會(huì)需要一個(gè)工作流,這個(gè)項(xiàng)目會(huì)需要一個(gè)訂單系統(tǒng),或者一個(gè)文檔庫,等等。通常,每個(gè)子系統(tǒng)可以對應(yīng)一個(gè)
     在做概要設(shè)計(jì)的時(shí)候,出類關(guān)系和ER簡圖。 通常來說,此時(shí)不能確定所有的字段,但是會(huì)有哪些表,有哪些主外鍵依賴,有哪些地方應(yīng)該需要存儲(chǔ)過程和觸發(fā)器的輔助,等等。
     詳細(xì)設(shè)計(jì)時(shí)盡可能將數(shù)據(jù)庫結(jié)構(gòu)完全固定。 盡管現(xiàn)代開發(fā)工具不斷提升XP能力,重構(gòu)越來越簡單。數(shù)據(jù)庫的重構(gòu)仍然是一件牽一發(fā)而動(dòng)全身的事情,畢竟數(shù)據(jù)庫是信息存儲(chǔ)的根本。大廈樓頂加個(gè)小花園容易,把地基下面的承重柱子拔出來換兩根試試?

重視SQL

     近年來ORM發(fā)展很快,幾乎每個(gè)框架都要提供這個(gè)功能,以至于會(huì)有些菜鳥認(rèn)為“ORM”會(huì)淘汰SQL語言。
     這是一塊試金石,如果你有這樣的感覺,應(yīng)該考慮認(rèn)真評估一下自己在這個(gè)領(lǐng)域是不是太菜了。
     SQL不是一種編程語言這么簡單,SQL代表的是一種與應(yīng)用開發(fā)語言完全不同的思想。面向集合,過程無關(guān),著眼于規(guī)則定義。可以說,SQL是FP High Order計(jì)算的最成功應(yīng)用,也可以說,SQL是一種靜態(tài)強(qiáng)類型的MapReduce語言。
看,換上時(shí)髦的名詞,會(huì)不會(huì)讓你覺得它上等起來了?
     在應(yīng)用層語言慘烈競爭的同時(shí),SQL語言壓倒了同時(shí)代出現(xiàn)的其他關(guān)系型數(shù)據(jù)庫操作語言,在這個(gè)擁有巨大利潤的領(lǐng)域占據(jù)了絕對統(tǒng)治地位。即使桀驁不馴的 Postgres,也在1995年變身為PostgrSQL。這一過程,并非像VC淘汰BC那么多盤外招,而是長時(shí)間爭議與選擇的結(jié)果。
對于信息操作規(guī)則定義,SQL幾乎是最好的表達(dá)方式。接近自然語言,高度可讀,并且非常利于優(yōu)化。
     打個(gè)比方,一個(gè)基于過程語言的上帝,這樣說:
     * 構(gòu)造一個(gè)光源對象
     * 構(gòu)造一個(gè)能源對象
     * 調(diào)用光源對象方法,設(shè)置能源
     * 調(diào)用光源對象的發(fā)光方法,傳入照明范圍內(nèi)的對象列表
     基于SQL的上帝說,要有光。
     當(dāng)然,在這位老兄背后,要有打雜的小弟去完成插電點(diǎn)燈的事情,但是作為上帝,什么活都自己做了,要天使干什么?
     看看那些應(yīng)用層語言的list comprehensions(列表推導(dǎo)式)。不止一次我想要為Python實(shí)現(xiàn)一個(gè)基于存儲(chǔ)層的列表推導(dǎo)式實(shí)現(xiàn),都可恥的失敗了。
     當(dāng)然,我承認(rèn)這跟跟人能力有關(guān),我不是Gudio。
     看看LINQ,不管如何吹噓,它就是一個(gè)抽象出I/O的SQL。我見過一些人激烈的貶低SQL,抬高ORM,同時(shí)又對LINQ頂禮膜拜,這可真夠分裂的。
     ORM對應(yīng)用層編程效率的提升是客觀的,無需回避。但是隨著你數(shù)據(jù)操作越來越精細(xì)和復(fù)雜,就越來越需要通過規(guī)則定義來抽象High Order I/O過程。當(dāng)你轉(zhuǎn)了一圈兒回來,會(huì)發(fā)現(xiàn)自己又在寫SQL。
     想想Hibernate的HQL,想想C#的LINQ。
     計(jì)算機(jī)不會(huì)變魔術(shù)。想讓它做事更聰明,就需要你這個(gè)馭者更加聰明才行。
     好的工具和方法可以給你帶來更高的能力系數(shù),但是記住,一個(gè)乘法計(jì)算,僅有一頭大是不夠的。
     不懂SQL的人,是不能駕馭好ORM的。

與ORM做朋友

     ORM對于開發(fā)工作,無疑是有好處的。我的朋友沈葳說,人腦能組織和分析的事務(wù)是有限的,所以代碼越短,越有利于提高代碼質(zhì)量。從這個(gè)角度講,ORM是非常重要的開發(fā)工具,其意義不亞于C API 函數(shù)集到GUI 框架的進(jìn)步。
要想讓ORM充分發(fā)揮威力,有時(shí)候需要從數(shù)據(jù)庫設(shè)計(jì)時(shí)就做出一定妥協(xié)。
     例如,你往往會(huì)需要加入自增標(biāo)識列,會(huì)放棄一些精巧但是不利于ORM訪問的依賴設(shè)定,甚至要放棄一些漂亮的命名(它們在應(yīng)用層語言中是保留字,但是你用的ORM不懂如何規(guī)避)。
     但是,這往往是必要的。就像建筑師向氣候和建筑材料妥協(xié)一樣。
     在ORM默認(rèn)的自增字段外,也許你還需要基于業(yè)務(wù)規(guī)則的唯一約束,那么額外加索引。
     好的ORM會(huì)幫助你方便的查詢數(shù)據(jù)字典,生成對象映射,跟蹤數(shù)據(jù)變更,提供數(shù)據(jù)完整性的應(yīng)用層檢查,構(gòu)造兩階段提交事務(wù),減少不必要的I/O。
     同樣,不懂得運(yùn)用ORM,也可能會(huì)破壞數(shù)據(jù)完整性,降低數(shù)據(jù)訪問速度,甚至造成數(shù)據(jù)庫死鎖。作為項(xiàng)目開發(fā)人員,應(yīng)該將ORM視為朋友而不是負(fù)擔(dān)。

合理分層

     過去,流行使用復(fù)雜的數(shù)據(jù)庫設(shè)計(jì),將業(yè)務(wù)規(guī)則存儲(chǔ)于數(shù)據(jù)庫的存儲(chǔ)過程。現(xiàn)在,又流行拋棄數(shù)據(jù)層的一切約束,所有的規(guī)則都放在應(yīng)用層。
     這兩者都不合理,除了應(yīng)用需求的影響,前者與Oracle的廣告部宣傳有關(guān),后者與MySQL陣營的鼓動(dòng)有關(guān)。背后都有一些不合理的力量推動(dòng)。
每一層應(yīng)該保證自己的完整性,這才是分層的意義。那么,在數(shù)據(jù)庫層,應(yīng)該保證數(shù)據(jù)的完整性。
     數(shù)據(jù)庫備份出來,再恢復(fù)進(jìn)去,應(yīng)該可以得到所有的業(yè)務(wù)信息。
     直接向數(shù)據(jù)庫導(dǎo)入數(shù)據(jù),應(yīng)該可以有完整的數(shù)據(jù)規(guī)則保護(hù)。
     數(shù)據(jù)庫里保存的,不僅僅是表和記錄,應(yīng)該是完整的持久性信息。
     從這個(gè)角度講,配置文件和應(yīng)用層代碼中不應(yīng)該有任何業(yè)務(wù)數(shù)據(jù)定義,這些信息都應(yīng)該是數(shù)據(jù)字典表。如果出現(xiàn)了這種配置文件,大多數(shù)情況下都是愚蠢的錯(cuò)誤。
     實(shí)際上,包括Web網(wǎng)站常見的附件上傳,都應(yīng)該保存在數(shù)據(jù)庫中。
     獨(dú)立的I/O文件存儲(chǔ)、包括將外鍵約束轉(zhuǎn)移到應(yīng)用層,往往是因?yàn)閷π阅艿耐讌f(xié)。以及,這里面確實(shí)存在MySQL陣營在推廣過程中的一些不道德的宣傳。
     有效利用數(shù)據(jù)庫功能,可以提高應(yīng)用層的開發(fā)速度,簡化代碼結(jié)構(gòu),使得數(shù)據(jù)存儲(chǔ)更安全。這通常仰賴與設(shè)計(jì)人員的經(jīng)驗(yàn),根據(jù)項(xiàng)目的具體需求進(jìn)行調(diào)整。
     基于這個(gè)原則,合理利用數(shù)據(jù)庫功能,編寫存儲(chǔ)過程,觸發(fā)器,調(diào)校索引,都是必要的。
     我敢打賭,隨著MySQL實(shí)現(xiàn)越來越多的功能,它的宣傳材料上會(huì)越來越多的出現(xiàn)以前被MySQL所摒棄的復(fù)雜設(shè)計(jì)理念,并且宣稱這是MySQL所獨(dú)創(chuàng)或一貫倡導(dǎo)的。

收集整理常見的模式

     在設(shè)計(jì)模式提出這么多年,在關(guān)系型數(shù)據(jù)庫問世如此之久后,我很驚訝的一件事就是數(shù)據(jù)庫設(shè)計(jì)模式仍然是一個(gè)相當(dāng)冷門的領(lǐng)域。實(shí)際上,關(guān)系數(shù)據(jù)庫的模式也有很多 可循之規(guī)。例如用戶信息(HR或CRM)、工作流,權(quán)限管理(如RBAC),訂單等等,都有相當(dāng)成熟的行業(yè)經(jīng)驗(yàn)和時(shí)間,往往只要修改一些字段名,或者在關(guān) 鍵架構(gòu)的基礎(chǔ)上加以擴(kuò)展,就可以很好的用于實(shí)踐。
     每一個(gè)有志于成為高水平設(shè)計(jì)人員的開發(fā)者,都應(yīng)該積極的收集自己體會(huì)到的數(shù)據(jù)庫設(shè)計(jì)模式,積極的與同行交流。
     這方面,Oracle的示例Schema,Postgres的示例數(shù)據(jù)庫項(xiàng)目(在Soureforge上可以找到),都是很好的例子。相對來說,微軟在MSSQL和Access中提供的示例庫更為輕量和簡單,也是作為入門的不錯(cuò)借鑒。