數(shù)據(jù)庫(kù)設(shè)計(jì)的基本方法
數(shù)據(jù)庫(kù)設(shè)計(jì)是建立數(shù)據(jù)庫(kù)及其應(yīng)用系統(tǒng)的核心和基礎(chǔ),它要求對(duì)于指定的應(yīng)用環(huán)境,構(gòu)造出較優(yōu)的數(shù)據(jù)庫(kù)模式,建立起數(shù)據(jù)庫(kù)應(yīng)用系統(tǒng),并使系統(tǒng)能有效地存儲(chǔ)數(shù)據(jù),滿足用戶的各種應(yīng)用需求。一般按照規(guī)范化的設(shè)計(jì)方法,常將數(shù)據(jù)庫(kù)設(shè)計(jì)分為若干階段:
系統(tǒng)規(guī)劃階段主要是確定系統(tǒng)的名稱、范圍;確定系統(tǒng)開發(fā)的目標(biāo)功能和性能;確定系統(tǒng)所需的資源;估計(jì)系統(tǒng)開發(fā)的成本;確定系統(tǒng)實(shí)施計(jì)劃及進(jìn)度;分析估算系統(tǒng)可能達(dá)到的效益;確定系統(tǒng)設(shè)計(jì)的原則和技術(shù)路線等。對(duì)分布式數(shù)據(jù)庫(kù)系統(tǒng),還應(yīng)分析用戶環(huán)境及網(wǎng)絡(luò)條件,以選擇和建立系統(tǒng)的網(wǎng)絡(luò)結(jié)構(gòu)。
需求分析階段要在用戶調(diào)查的基礎(chǔ)上,通過分析,逐步明確用戶對(duì)系統(tǒng)的需求,包括數(shù)據(jù)需求和圍繞這些數(shù)據(jù)的業(yè)務(wù)處理需求。通過對(duì)組織、部門、企業(yè)等進(jìn)行詳細(xì)調(diào)查,在了解現(xiàn)行系統(tǒng)的概況、確定新系統(tǒng)功能的過程中,收集支持系統(tǒng)目標(biāo)的基礎(chǔ)數(shù)據(jù)及其處理方法。
概念設(shè)計(jì)階段要產(chǎn)生反映企業(yè)各組織信息需求的數(shù)據(jù)庫(kù)概念結(jié)構(gòu),即概念模型。概念模型必須具備豐富的語義表達(dá)能力、易于交流和理解、易于變動(dòng)、易于向各種數(shù)據(jù)模型轉(zhuǎn)換、易于從概念模型導(dǎo)出與DBMS有關(guān)的邏輯模型等特點(diǎn)。
邏輯設(shè)計(jì)階段除了要把E-R圖的實(shí)體和聯(lián)系類型,轉(zhuǎn)換成選定的DBMS支持的數(shù)據(jù)類型,還要設(shè)計(jì)子模式并對(duì)模式進(jìn)行評(píng)價(jià),最后為了使模式適應(yīng)信息的不同表示,需要優(yōu)化模式。
物理設(shè)計(jì)階段的主要任務(wù)是對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)在物理設(shè)備上的存放結(jié)構(gòu)和存取方法進(jìn)行設(shè)計(jì)。數(shù)據(jù)庫(kù)物理結(jié)構(gòu)依賴于給定的計(jì)算機(jī)系統(tǒng),而且與具體選用的DBMS密切相關(guān)。物理設(shè)計(jì)常常包括某些操作約束,如響應(yīng)時(shí)間與存儲(chǔ)要求等。
系統(tǒng)實(shí)施階段主要分為建立實(shí)際的數(shù)據(jù)庫(kù)結(jié)構(gòu);裝入試驗(yàn)數(shù)據(jù)對(duì)應(yīng)用程序進(jìn)行測(cè)試;裝入實(shí)際數(shù)據(jù)建立實(shí)際數(shù)據(jù)庫(kù)三個(gè)步驟。
另外,在數(shù)據(jù)庫(kù)的設(shè)計(jì)過程中還包括一些其他設(shè)計(jì),如數(shù)據(jù)庫(kù)的安全性、完整性、一致性和可恢復(fù)性等方面的設(shè)計(jì),不過,這些設(shè)計(jì)總是以犧牲效率為代價(jià)的,設(shè)計(jì)人員的任務(wù)就是要在效率和盡可能多的功能之間進(jìn)行合理的權(quán)衡。
數(shù)據(jù)庫(kù)編程示例
在本次講座中,我們以Delphi5為開發(fā)工具,標(biāo)準(zhǔn)的paradox表為后臺(tái)數(shù)據(jù)庫(kù),來向大家介紹如何進(jìn)行最簡(jiǎn)單的數(shù)據(jù)庫(kù)編程。在本例中,我們將實(shí)現(xiàn)對(duì)一個(gè)數(shù)據(jù)表單的添加、修改、刪除以及對(duì)表中數(shù)據(jù)進(jìn)行查詢的功能。
本例所使用的計(jì)算機(jī)軟硬件環(huán)境為:Windows NT 4.0 Server,Borland Delphi 5,PIII550,256M內(nèi)存。當(dāng)然啦,一般的朋友在Win98的環(huán)境下或者Win2000的環(huán)境下都可以按照本例的步驟來編程序的。筆者的機(jī)器主要用作服務(wù)器,所以就在服務(wù)器上編啦。廢話少說,現(xiàn)在開始:
在正式編寫程序之前要有許多準(zhǔn)備工作,這當(dāng)然包括Borland Delphi 5的安裝。Delphi5自身帶了對(duì)標(biāo)準(zhǔn)數(shù)據(jù)表paradox的驅(qū)動(dòng)(和一些fox系列數(shù)據(jù)庫(kù)軟件的.db文件是兼容的,這些文件都可以由Delphi直接讀取)。
程序編寫的第一步首先是數(shù)據(jù)表(即.db文件)的建立。在這一步要用到Delphi自帶的一個(gè)工具叫Database Desktop。這個(gè)工具主要用來對(duì)表格進(jìn)行操作,它不僅可以操作像.db文件這樣的標(biāo)準(zhǔn)表格,還可以訪問像Oracle、SQL Server等大型數(shù)據(jù)庫(kù)的文件,功能可以說是很強(qiáng)大的。打開Database Desktop,選擇File-New-Table,然后選擇paradox7.
在FieldName里面填寫你要建立的表單的屬性名稱,如圖所示,本例中要建立的是人員管理表,所以建立的屬性為編號(hào)、姓名、性別、工作單位、工資、備注等。除工資之外的屬性都是字符串類型,即圖中Type項(xiàng)所選擇的A(Alpha),而Size則是指的字符串的長(zhǎng)度,屬性值的長(zhǎng)度是根據(jù)屬性的具體意義來確定的,例如“性別”選擇只有“男”和“女”,一個(gè)漢字的長(zhǎng)度,那么性別屬性的長(zhǎng)度設(shè)置成2就可以了(一個(gè)漢字等于兩個(gè)字符的長(zhǎng)度)。而工資屬性是數(shù)值類型的,在paradox中用Number來標(biāo)記,即和前面對(duì)應(yīng)的一個(gè)“N”。屬性設(shè)置完畢后,選擇Save As存盤。本例中,所建立的數(shù)據(jù)表格的存放路徑和文件名為:D:Program FilesBorlandDatabase DesktopWorkDir
yb.db(人員表)。
這樣就完成了第一步,這一步只是編程之前的準(zhǔn)備工作,也是后面針對(duì)這個(gè)表單編寫數(shù)據(jù)庫(kù)應(yīng)用程序的基礎(chǔ)。下面要進(jìn)行的就是實(shí)際的程序編寫了,請(qǐng)打開Borland Delphi 5。
在缺省的Form1上放置這樣一些控件(控件的位置就不用俺再羅嗦了吧?):TdataBase、Ttable、TdataSourse、TdbGrid、Tlabel、TdbNavigator。按照你喜歡的界面風(fēng)格來放置這些控件,下一步就是控件屬性的設(shè)置啦,這一步也是本例中最關(guān)鍵的一步,因?yàn)樵S多網(wǎng)友對(duì)使用前端開發(fā)工具對(duì)后臺(tái)數(shù)據(jù)庫(kù)進(jìn)行編程比較生疏,也就是在這個(gè)地方。
我們將Tdatabase的DataBaseName屬性設(shè)置成Mydb(這個(gè)名字可以隨便起),drivername為STANDARD(表明驅(qū)動(dòng)的是paradox表),params屬性為path=D:Program FilesBorlandDatabase DesktopWorkDir(存放.db文件的路徑,前面已經(jīng)提到),connected設(shè)為true(和數(shù)據(jù)表建立起連接),其他屬性使用缺省值就可以了?! ?
然后將Table的Databasename屬性設(shè)為Mydb(即Database1的DataBaseName屬性),tablename為ryb.db(即數(shù)據(jù)表的名字)Active屬性設(shè)置為True(打開表單,便于編寫程序)。
接著將Datasourse1的dataset屬性設(shè)為為Table1(Ttable的name屬性值)。另外DBNavigator1和DbGrid1的datasourse屬性都設(shè)為datasourse1。Tlabel的caption屬性設(shè)為“數(shù)據(jù)庫(kù)編程示例”,如圖所示(可以根據(jù)個(gè)人喜好修改字體,使它變得好看一些)?! ?>這樣控件的屬性就設(shè)置完畢了,注意到dbgrid里面的“編號(hào)”、“姓名”等欄都是數(shù)據(jù)表中的屬性,在ryb.db里面定義,而不是在前端的delphi程序里面定義。點(diǎn)擊run按鈕之后,這個(gè)簡(jiǎn)單的程序就運(yùn)行了?! ?
這是通過Dbnavigator上的按鈕就可以控制對(duì)表單進(jìn)行數(shù)據(jù)的添加、刪除、修改了(將DbNavigator的ShowHint屬性改為True的話可以在運(yùn)行時(shí)看到各個(gè)按鈕的提示)?!?BR>
至此,我們實(shí)現(xiàn)了使用Delphi這種快速開發(fā)工具編寫簡(jiǎn)單的數(shù)據(jù)庫(kù)應(yīng)用程序, 細(xì)心的讀者可能注意到了,到目前為止連一行程序都沒有寫,確實(shí)是這樣,這就是第四代程序設(shè)計(jì)語言(可視化的編程語言)給我們帶來的好處。當(dāng)然啦,要編寫比較復(fù)雜的程序不寫代碼是不可能的,下面我們將在上面的基礎(chǔ)上實(shí)現(xiàn)查詢的功能,并給大家附上源程序。在Form增加如下控件:一個(gè)Tquery、一個(gè)Tbutton、4個(gè)Tlabel、2個(gè)Tedit。并將Tlabel的屬性進(jìn)行下圖所示的修改(新增加的控件都在黑色方框中):
將Query1的DataBaseName屬性也設(shè)置成MyDb(和Table一樣),SQL屬性中寫入Select* from ryb,RequestLive設(shè)為True(這樣可以對(duì)Query里面的數(shù)據(jù)進(jìn)行修改),然后將Active屬性設(shè)為True。最后將DataSourse1的DataSet屬性改為query1(此時(shí)Table1控件可以從Form上去掉了)。
雙擊Button1,在程序編輯器里面寫入如下代碼:
Query1.close;
Query1.Sql.clear;
Query1.SQL.Add(Select * from ryb);
Query1.sql.add(where 姓名 like :xm and 工資 > :gz);
Query1.ParamByName(xm).asstring:=%+Edit1.text+%;
If (Edit2.text<>’’)then
Query1.ParamByName(gz).value:=strtofloat(Edit2.text);
Query1.prepare;
Query1.open; >
這樣就完成了查詢代碼的編寫,具體各條語句所引用的函數(shù)的含義大家可以參考Delphi的幫助文檔,在此就不作贅述了。另外在編寫查詢程序時(shí),涉及到一些關(guān)于數(shù)據(jù)庫(kù)查詢語言SQL的知識(shí),在后面的文章中我們將有比較詳細(xì)的介紹?! ?>此時(shí)再執(zhí)行這個(gè)程序,就編程的具有查詢功能的數(shù)據(jù)庫(kù)程序。在Edit1和Edit2中輸入相應(yīng)的查詢條件,然后點(diǎn)擊Button1(確定),程序就執(zhí)行相應(yīng)的查詢操作,查詢到的結(jié)果將在DBGrid1中顯示?! ?
到此為止,我們就實(shí)現(xiàn)了使用Borland Delphi5操縱paradox數(shù)據(jù)表的程序示例,當(dāng)然,這個(gè)程序是非常簡(jiǎn)單的,它實(shí)現(xiàn)的只是數(shù)據(jù)庫(kù)操作的最基本功能。而且這個(gè)程序只是在單擊環(huán)境下運(yùn)行的,在設(shè)計(jì)的時(shí)候,我們還不需要考慮一些例如并發(fā)性的問題?,F(xiàn)在比較成熟的是在客戶機(jī)/服務(wù)器結(jié)構(gòu)下和瀏覽器/服務(wù)器下對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作,而且隨著網(wǎng)絡(luò)功能的加入,許多新的問題也帶入進(jìn)來,在后面我們將舉這方面的例子,請(qǐng)大家靜候佳音吧!
一個(gè)好的數(shù)據(jù)庫(kù)產(chǎn)品不等于就有一個(gè)好的應(yīng)用系統(tǒng),如果不能設(shè)計(jì)一個(gè)合理的數(shù)據(jù)庫(kù)模型,不僅會(huì)增加客戶端和服務(wù)器段程序的編程和維護(hù)的難度,而且將會(huì)影響系統(tǒng)實(shí)際運(yùn)行的性能。一般來講,在一個(gè)MIS系統(tǒng)分析、設(shè)計(jì)、測(cè)試和試運(yùn)行階段,因?yàn)閿?shù)據(jù)量較小,設(shè)計(jì)人員和測(cè)試人員往往只注意到功能的實(shí)現(xiàn),而很難注意到性能的薄弱之處,等到系統(tǒng)投入實(shí)際運(yùn)行一段時(shí)間后,才發(fā)現(xiàn)系統(tǒng)的性能在降低,這時(shí)再來考慮提高系統(tǒng)性能則要花費(fèi)更多的人力物力,而整個(gè)系統(tǒng)也不可避免的形成了一個(gè)打補(bǔ)丁工程。筆者依據(jù)多年來設(shè)計(jì)和使用數(shù)據(jù)庫(kù)的經(jīng)驗(yàn),提出以下一些設(shè)計(jì)準(zhǔn)則,供同仁們參考。
命名的規(guī)范
不同的數(shù)據(jù)庫(kù)產(chǎn)品對(duì)對(duì)象的命名有不同的要求,因此,數(shù)據(jù)庫(kù)中的各種對(duì)象的命名、后臺(tái)程序的代碼編寫應(yīng)采用大小寫敏感的形式,各種對(duì)象命名長(zhǎng)度不要超過30個(gè)字符,這樣便于應(yīng)用系統(tǒng)適應(yīng)不同的數(shù)據(jù)庫(kù)。
游標(biāo)(Cursor)的慎用
游標(biāo)提供了對(duì)特定集合中逐行掃描的手段,一般使用游標(biāo)逐行遍歷數(shù)據(jù),根據(jù)取出的數(shù)據(jù)不同條件進(jìn)行不同的操作。尤其對(duì)多表和大表定義的游標(biāo)(大的數(shù)據(jù)集合)循環(huán)很容易使程序進(jìn)入一個(gè)漫長(zhǎng)的等特甚至死機(jī),筆者在某市《住房公積金管理系統(tǒng)》進(jìn)行日終帳戶滾積數(shù)計(jì)息處理時(shí),對(duì)一個(gè)10萬個(gè)帳戶的游標(biāo)處理導(dǎo)致程序進(jìn)入了一個(gè)無限期的等特(后經(jīng)測(cè)算需48個(gè)小時(shí)才能完成)(硬件環(huán)境:Alpha/4000 128Mram ,Sco Unix,Sybase 11.0),后根據(jù)不同的條件改成用不同的UPDATE語句得以在二十分鐘之內(nèi)完成。
示例如下:
Declare Mycursor cursor for select count_no from COUNT
Open Mycursor
Fetch Mycursor into @vcount_no
While (@@sqlstatus=0)
Begin
If @vcount_no=’’ 條件1
操作1
If @vcount_no=’’ 條件2
操作2
。。。
Fetch Mycursor into @vcount_no
End
。。。
。。。
改為
Update COUNT set 操作1 for 條件1
Update COUNT set 操作2 for 條件2
。。。
。。。
在有些場(chǎng)合,有時(shí)也非得使用游標(biāo),此時(shí)也可考慮將符合條件的數(shù)據(jù)行轉(zhuǎn)入臨時(shí)表中,再對(duì)臨時(shí)表定義游標(biāo)進(jìn)行操作,可時(shí)性能得到明顯提高。筆者在某地市〈電信收費(fèi)系統(tǒng)〉數(shù)據(jù)庫(kù)后臺(tái)程序設(shè)計(jì)中,對(duì)一個(gè)表(3萬行中符合條件的30多行數(shù)據(jù))進(jìn)行游標(biāo)操作(硬件環(huán)境:PC服務(wù)器,PII266 64Mram ,NT4.0 Ms Sqlserver 6.5)。 示例如下:
Create #tmp /* 定義臨時(shí)表 */
( 字段1
字段2
。。。
)
Insert into #tmp select * from TOTAL where 條件 /* TOTAL中3萬行 符合條件只有幾十行 */
Declare Mycursor cursor for select * from #tmp
/*對(duì)臨時(shí)表定義游標(biāo)*/
。。。
索引(Index)的使用原則
創(chuàng)建索引一般有以下兩個(gè)目的:維護(hù)被索引列的唯一性和提供快速訪問表中數(shù)據(jù)的策略。大型數(shù)據(jù)庫(kù)有兩種索引即簇索引和非簇索引,一個(gè)沒有簇索引的表是按堆結(jié)構(gòu)存儲(chǔ)數(shù)據(jù),所有的數(shù)據(jù)均添加在表的尾部,而建立了簇索引的表,其數(shù)據(jù)在物理上會(huì)按照簇索引鍵的順序存儲(chǔ),一個(gè)表只允許有一個(gè)簇索引,因此,根據(jù)B樹結(jié)構(gòu),可以理解添加任何一種索引均能提高按索引列查詢的速度,但會(huì)降低插入、更新、刪除操作的性能,尤其是當(dāng)填充因子(Fill Factor)較大時(shí)。所以對(duì)索引較多的表進(jìn)行頻繁的插入、更新、刪除操作,建表和索引時(shí)因設(shè)置較小的填充因子,以便在各數(shù)據(jù)頁中留下較多的自由空間,減少頁分割及重新組織的工作。
數(shù)據(jù)的一致性和完整性
為了保證數(shù)據(jù)庫(kù)的一致性和完整性,設(shè)計(jì)人員往往會(huì)設(shè)計(jì)過多的表間關(guān)聯(lián)(Relation),盡可能的降低數(shù)據(jù)的冗余。表間關(guān)聯(lián)是一種強(qiáng)制性措施,建立后,對(duì)父表(Parent Table)和子表(Child Table)的插入、更新、刪除操作均要占用系統(tǒng)的開銷,另外,最好不要用Identify 屬性字段作為主鍵與子表關(guān)聯(lián)。如果數(shù)據(jù)冗余低,數(shù)據(jù)的完整性容易得到保證,但增加了表間連接查詢的操作,為了提高系統(tǒng)的響應(yīng)時(shí)間,合理的數(shù)據(jù)冗余也是必要的。使用規(guī)則(Rule)和約束(Check)來防止系統(tǒng)操作人員誤輸入造成數(shù)據(jù)的錯(cuò)誤是設(shè)計(jì)人員的另一種常用手段,但是,不必要的規(guī)則和約束也會(huì)占用系統(tǒng)的不必要開銷,需要注意的是,約束對(duì)數(shù)據(jù)的有效性驗(yàn)證要比規(guī)則快。所有這些,設(shè)計(jì)人員在設(shè)計(jì)階段應(yīng)根據(jù)系統(tǒng)操作的類型、頻度加以均衡考慮。
事務(wù)的陷阱
事務(wù)是在一次性完成的一組操作。雖然這些操作是單個(gè)的操作,SQL Server能夠保證這組操作要么全部都完成,要么一點(diǎn)都不做。正是大型數(shù)據(jù)庫(kù)的這一特性,使得數(shù)據(jù)的完整性得到了極大的保證。
眾所周知,SQL Server為每個(gè)獨(dú)立的SQL語句都提供了隱含的事務(wù)控制,使得每個(gè)DML的數(shù)據(jù)操作得以完整提交或回滾,但是SQL Server還提供了顯式事務(wù)控制語句
—- BEGIN TRANSACTION 開始一個(gè)事務(wù)
—- COMMIT TRANSACTION 提交一個(gè)事務(wù)
—- ROLLBACK TRANSACTION 回滾一個(gè)事務(wù)
—- 事務(wù)可以嵌套,可以通過全局變量@@trancount檢索到連接的事務(wù)處理嵌套層次。
需要加以特別注意并且極容易使編程人員犯錯(cuò)誤的是,每個(gè)顯示或隱含的事物開始都使得該變量加1,每個(gè)事務(wù)的提交使該變量減1,每個(gè)事務(wù)的回滾都會(huì)使得該變量置0,而只有當(dāng)該變量為0時(shí)的事務(wù)提交(最后一個(gè)提交語句時(shí)),這時(shí)才把物理數(shù)據(jù)寫入磁盤。
數(shù)據(jù)庫(kù)性能調(diào)整
在計(jì)算機(jī)硬件配置和網(wǎng)絡(luò)設(shè)計(jì)確定的情況下,影響到應(yīng)用系統(tǒng)性能的因素不外乎為數(shù)據(jù)庫(kù)性能和客戶端程序設(shè)計(jì)。而大多數(shù)數(shù)據(jù)庫(kù)設(shè)計(jì)員采用兩步法進(jìn)行數(shù)據(jù)庫(kù)設(shè)計(jì):首先進(jìn)行邏輯設(shè)計(jì),而后進(jìn)行物理設(shè)計(jì)。數(shù)據(jù)庫(kù)邏輯設(shè)計(jì)去除了所有冗余數(shù)據(jù),提高了數(shù)據(jù)吞吐速度,保證了數(shù)據(jù)的完整性,清楚地表達(dá)數(shù)據(jù)元素之間的關(guān)系。而對(duì)于多表之間的關(guān)聯(lián)查詢(尤其是大數(shù)據(jù)表)時(shí),其性能將會(huì)降低,同時(shí)也提高了客 戶端程序的編程難度,因此,物理設(shè)計(jì)需折衷考慮,根據(jù)業(yè)務(wù)規(guī)則,確定對(duì)關(guān)聯(lián)表的數(shù)據(jù)量大小、數(shù)據(jù)項(xiàng)的訪問頻度,對(duì)此類數(shù)據(jù)表頻繁的關(guān)聯(lián)查詢應(yīng)適當(dāng)提高數(shù)據(jù)冗余設(shè)計(jì)。
數(shù)據(jù)類型的選擇
數(shù)據(jù)類型的合理選擇對(duì)于數(shù)據(jù)庫(kù)的性能和操作具有很大的影響,有關(guān)這方面的書籍也有不少的闡述,這里主要介紹幾點(diǎn)經(jīng)驗(yàn)。
Identify字段不要作為表的主鍵與其它表關(guān)聯(lián),這將會(huì)影響到該表的數(shù)據(jù)遷移。
Text 和Image字段屬指針型數(shù)據(jù),主要用來存放二進(jìn)制大型對(duì)象(BLOB)。這類數(shù)據(jù)的操作相比其它數(shù)據(jù)類型較慢,因此要避開使用。
日期型字段的優(yōu)點(diǎn)是有眾多的日期函數(shù)支持,因此,在日期的大小比較、加減操作上非常簡(jiǎn)單。但是,在按照日期作為條件的查詢操作也要用函數(shù),相比其它數(shù)據(jù)類型速度上就慢許多,因?yàn)橛煤瘮?shù)作為查詢的條件時(shí),服務(wù)器無法用先進(jìn)的性能策略來優(yōu)化查詢而只能進(jìn)行表掃描遍歷每行。
例如:要從DATA_TAB1中(其中有一個(gè)名為DATE的日期字段)查詢1998年的所有記錄。