新手淺談數據庫中的設計技巧(一)
2024-07-21 02:35:47
供稿:網友
說到數據庫,我認為不能不先談數據結構。1996年,在我初入大學學習計算機編程時,當時的老師就告訴我們說:計算機程序=數據結構+算法。盡管現在的程序開發已由面向過程為主逐步過渡到面向對象為主,但我還是深深贊同8年前老師的告訴我們的公式:計算機程序=數據結構+算法。 面向對象的程序開發,要做的第一件事就是,先分析整個程序中需處理的數據,從中提取出抽象模板,以這個抽象模板設計類,再在其中逐步添加處理其數據的函數(即算法),最后,再給類中的數據成員和函數劃分訪問權限,從而實現封裝。
數據庫的最初雛形據說源自美國一個奶牛場的記賬薄(紙質的,由此可見,數據庫并不一定是存儲在電腦里的數據^_^),里面記錄的是該奶牛場的收支賬目,程序員在將其整理、錄入到電腦中時從中受到啟發。當按照規定好的數據結構所采集到的數據量大到一定程度后,出于程序執行效率的考慮,程序員將其中的檢索、更新維護等功能分離出來,做成單獨調用的模塊,這個模塊后來就慢慢發展、演變成現在我們所接觸到的數據庫治理系統(dbms)——程序開發中的一個重要分支。
下面進入正題,首先按我個人所接觸過的程序給數據庫設計人員的功底分一下類:
1、沒有系統學習過數據結構的程序員。這類程序員的作品往往只是他們的即興玩具,他們往往習慣只設計有限的幾個表,實現某類功能的數據全部塞在一個表中,各表之間幾乎毫無關聯。網上不少的免費治理軟件都是這樣的東西,當程序功能有限,數據量不多的時候,其程序運行起來沒有什么問題,但是假如用其治理比較重要的數據,風險性非常大。
2、系統學習過數據結構,但是還沒有開發過對程序效率要求比較高的治理軟件的程序員。這類人多半剛從學校畢業不久,他們在設計數據庫表結構時,嚴格按照教科書上的規定,死扣e-r圖和3nf(別灰心,所有的數據庫設計高手都是從這一步開始的)。他們的作品,對于一般的access型輕量級的治理軟件,已經夠用。但是一旦該系統需要添加新功能,原有的數據庫表差不多得進行大換血。
3、第二類程序員,在經歷過數次程序效率的提升,以及功能升級的折騰后,終于升級成為數據庫設計的老鳥,第一類程序員眼中的高人。這類程序員可以勝任二十個表以上的中型商業數據治理系統的開發工作。他們知道該在什么樣的情況下保留一定的冗余數據來提高程序效率,而且其設計的數據庫可拓展性較好,當用戶需要添加新功能時,原有數據庫表只需做少量修改即可。
?。?、在經歷過上十個類似數據庫治理軟件的重復設計后,第三類程序員中堅持下來沒有轉行,而是希望從中找出“偷懶”竅門的有心人會慢慢覺悟,從而完成量變到質變的轉換。他們所設計的數據庫表結構有一定的遠見,能夠猜測到未來功能升級所需要的數據,從而預先留下伏筆。這類程序員目前大多晉級成數據挖掘方面的高級軟件開發人員。
?。?、第三類程序員或第四類程序員,在對現有的各家數據庫治理系統的原理和開發都有一定的鉆研后,要么在其基礎上進行二次開發,要么自行開發一套有自主版權的通用數據庫治理系統。
我個人正處于第三類的末期,所以下面所列出的一些設計技巧只適合第二類和部分第三類數據庫設計人員。同時,由于我很少碰到有愛好在這方面深鉆下去的同行,所以文中難免出現錯誤和遺漏,在此先行聲明,歡迎大家指正,不要藏私哦8)
一、樹型關系的數據表
不少程序員在進行數據庫設計的時候都碰到過樹型關系的數據,例如常見的類別表,即一個大類,下面有若干個子類,某些子類又有子類這樣的情況。當類別不確定,用戶希望可以在任意類別下添加新的子類,或者刪除某個類別和其下的所有子類,而且預計以后其數量會逐步增長,此時我們就會考慮用一個數據表來保存這些數據。按照教科書上的教導,第二類程序員大概會設計出類似這樣的數據表結構:
類別表_1(type_table_1)
名稱 類型約束條件 說明
type_id int 無重復 類別標識,主鍵
type_name char(50) 不答應為空 類型名稱,不答應重復
type_father int 不答應為空 該類別的父類別標識,假如是頂節點的話設定為某個唯一值
這樣的設計短小精悍,完全滿足3nf,而且可以滿足用戶的所有要求。是不是這樣就行呢?答案是no!why?
我們來估計一下用戶希望如何羅列出這個表的數據的。對用戶而言,他當然期望按他所設定的層次關系一次羅列出所有的類別,例如這樣:
總類別
類別1
類別1.1
類別1.1.1
類別1.2
類別2
類別2.1
類別3
類別3.1
類別3.2
……
看看為了實現這樣的列表顯示(樹的先序遍歷),要對上面的表進行多少次檢索?注重,盡管類別1.1.1可能是在類別3.2之后添加的記錄,答案仍然是n 次。
這樣的效率對于少量的數據沒什么影響,但是日后類型擴充到數十條甚至上百條記錄后,單單列一次類型就要檢索數十次該表,整個程序的運行效率就不敢恭維了。或許第二類程序員會說,那我再建一個臨時數組或臨時表,專門保存類型表的先序遍歷結果,這樣只在第一次運行時檢索數十次,再次羅列所有的類型關系時就直接讀那個臨時數組或臨時表就行了。其實,用不著再去分配一塊新的內存來保存這些數據,只要對數據表進行一定的擴充,再對添加類型的數量進行一下約束就行了,要完成上面的列表只需一次檢索就行了。下面是擴充后的數據表結構:
類別表_2(type_table_2)
名稱 類型約束條件 說明
type_id int 無重復 類別標識,主鍵
type_name char(50) 不答應為空 類型名稱,不答應重復
type_father int 不答應為空 該類別的父類別標識,假如是頂節點的話設定為某個唯一值
type_layer char(6) 限定3層,初始值為000000 類別的先序遍歷,主要為減少檢索數據庫的次數
按照這樣的表結構,我們來看看上面例子記錄在表中的數據是怎樣的:
type_id type_name type_father type_layer
1 總類別 0 000000
2 類別1 1 010000
3 類別1.1 2 010100
4 類別1.2 2 010200
5 類別2 1 020000
6 類別2.1 5 020100
7 類別3 1 030000
8 類別3.1 7 030100
9 類別3.2 7 030200
10 類別1.1.1 3 010101
……
現在按type_layer的大小來檢索一下:select * from type_table_2 order by type_layer
列出記錄集如下:
type_id type_name type_father type_layer
1 總類別 0 000000
2 類別1 1 010000
3 類別1.1 2 010100
10 類別1.1.1 3 010101
4 類別1.2 2 010200
5 類別2 1 020000
6 類別2.1 5 020100
7 類別3 1 030000
8 類別3.1 7 030100
9 類別3.2 7 030200
……
現在列出的記錄順序正好是先序遍歷的結果。在控制顯示類別的層次時,只要對type_layer字段中的數值進行判定,每2位一組,如大于0則向右移2 個空格。當然,我這個例子中設定的限制條件是最多3層,每層最多可設99個子類別,只要按用戶的需求情況修改一下type_layer的長度和位數,即可更改限制層數和子類別數。其實,上面的設計不單單只在類別表中用到,網上某些可按樹型列表顯示的論壇程序大多采用類似的設計。
或許有人認為,type_table_2中的type_father字段是冗余數據,可以除去。假如這樣,在插入、刪除某個類別的時候,就得對 type_layer 的內容進行比較繁瑣的判定,所以我并沒有消去type_father字段,這也正符合數據庫設計中適當保留冗余數據的來降低程序復雜度的原則,后面我會舉一個故意增加數據冗余的案例。
二、商品信息表的設計
假設你是一家百貨公司電腦部的開發人員,某天老板要求你為公司開發一套網上電子商務平臺,該百貨公司有數千種商品出售,不過目前僅打算先在網上銷售數十種方便運輸的商品,當然,以后可能會陸續在該電子商務平臺上增加新的商品出售?,F在開始進行該平臺數據庫的商品信息表的設計。每種出售的商品都