国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

淺談數(shù)據(jù)庫設(shè)計技巧(上)

2024-07-21 02:10:56
字體:
供稿:網(wǎng)友
中國最大的web開發(fā)資源網(wǎng)站及技術(shù)社區(qū),
說到數(shù)據(jù)庫,我認(rèn)為不能不先談數(shù)據(jù)結(jié)構(gòu)。1996年,在我初入大學(xué)學(xué)習(xí)計算機(jī)編程時,當(dāng)時的老師就告訴我們說:計算機(jī)程序=數(shù)據(jù)結(jié)構(gòu)+算法。盡管現(xiàn)在的程序開發(fā)已由面向過程為主逐步過渡到面向?qū)ο鬄橹?,但我還是深深贊同8年前老師的告訴我們的公式:計算機(jī)程序=數(shù)據(jù)結(jié)構(gòu)+算法。面向?qū)ο蟮某绦蜷_發(fā),要做的第一件事就是,先分析整個程序中需處理的數(shù)據(jù),從中提取出抽象模板,以這個抽象模板設(shè)計類,再在其中逐步添加處理其數(shù)據(jù)的函數(shù)(即算法),最后,再給類中的數(shù)據(jù)成員和函數(shù)劃分訪問權(quán)限,從而實(shí)現(xiàn)封裝。

  數(shù)據(jù)庫的最初雛形據(jù)說源自美國一個奶牛場的記賬薄(紙質(zhì)的,由此可見,數(shù)據(jù)庫并不一定是存儲在電腦里的數(shù)據(jù)^_^),里面記錄的是該奶牛場的收支賬目,程序員在將其整理、錄入到電腦中時從中受到啟發(fā)。當(dāng)按照規(guī)定好的數(shù)據(jù)結(jié)構(gòu)所采集到的數(shù)據(jù)量大到一定程度后,出于程序執(zhí)行效率的考慮,程序員將其中的檢索、更新維護(hù)等功能分離出來,做成單獨(dú)調(diào)用的模塊,這個模塊后來就慢慢發(fā)展、演變成現(xiàn)在我們所接觸到的數(shù)據(jù)庫管理系統(tǒng)(dbms)——程序開發(fā)中的一個重要分支。

  下面進(jìn)入正題,首先按我個人所接觸過的程序給數(shù)據(jù)庫設(shè)計人員的功底分一下類:
 ?。?、沒有系統(tǒng)學(xué)習(xí)過數(shù)據(jù)結(jié)構(gòu)的程序員。這類程序員的作品往往只是他們的即興玩具,他們往往習(xí)慣只設(shè)計有限的幾個表,實(shí)現(xiàn)某類功能的數(shù)據(jù)全部塞在一個表中,各表之間幾乎毫無關(guān)聯(lián)。網(wǎng)上不少的免費(fèi)管理軟件都是這樣的東西,當(dāng)程序功能有限,數(shù)據(jù)量不多的時候,其程序運(yùn)行起來沒有什么問題,但是如果用其管理比較重要的數(shù)據(jù),風(fēng)險性非常大。
 ?。病⑾到y(tǒng)學(xué)習(xí)過數(shù)據(jù)結(jié)構(gòu),但是還沒有開發(fā)過對程序效率要求比較高的管理軟件的程序員。這類人多半剛從學(xué)校畢業(yè)不久,他們在設(shè)計數(shù)據(jù)庫表結(jié)構(gòu)時,嚴(yán)格按照教科書上的規(guī)定,死扣e-r圖和3nf(別灰心,所有的數(shù)據(jù)庫設(shè)計高手都是從這一步開始的)。他們的作品,對于一般的access型輕量級的管理軟件,已經(jīng)夠用。但是一旦該系統(tǒng)需要添加新功能,原有的數(shù)據(jù)庫表差不多得進(jìn)行大換血。
 ?。?、第二類程序員,在經(jīng)歷過數(shù)次程序效率的提升,以及功能升級的折騰后,終于升級成為數(shù)據(jù)庫設(shè)計的老鳥,第一類程序員眼中的高人。這類程序員可以勝任二十個表以上的中型商業(yè)數(shù)據(jù)管理系統(tǒng)的開發(fā)工作。他們知道該在什么樣的情況下保留一定的冗余數(shù)據(jù)來提高程序效率,而且其設(shè)計的數(shù)據(jù)庫可拓展性較好,當(dāng)用戶需要添加新功能時,原有數(shù)據(jù)庫表只需做少量修改即可。
  4、在經(jīng)歷過上十個類似數(shù)據(jù)庫管理軟件的重復(fù)設(shè)計后,第三類程序員中堅(jiān)持下來沒有轉(zhuǎn)行,而是希望從中找出“偷懶”竅門的有心人會慢慢覺悟,從而完成量變到質(zhì)變的轉(zhuǎn)換。他們所設(shè)計的數(shù)據(jù)庫表結(jié)構(gòu)有一定的遠(yuǎn)見,能夠預(yù)測到未來功能升級所需要的數(shù)據(jù),從而預(yù)先留下伏筆。這類程序員目前大多晉級成數(shù)據(jù)挖掘方面的高級軟件開發(fā)人員。
 ?。怠⒌谌惓绦騿T或第四類程序員,在對現(xiàn)有的各家數(shù)據(jù)庫管理系統(tǒng)的原理和開發(fā)都有一定的鉆研后,要么在其基礎(chǔ)上進(jìn)行二次開發(fā),要么自行開發(fā)一套有自主版權(quán)的通用數(shù)據(jù)庫管理系統(tǒng)。

  我個人正處于第三類的末期,所以下面所列出的一些設(shè)計技巧只適合第二類和部分第三類數(shù)據(jù)庫設(shè)計人員。同時,由于我很少碰到有興趣在這方面深鉆下去的同行,所以文中難免出現(xiàn)錯誤和遺漏,在此先行聲明,歡迎大家指正,不要藏私哦8)

  一、樹型關(guān)系的數(shù)據(jù)表
  不少程序員在進(jìn)行數(shù)據(jù)庫設(shè)計的時候都遇到過樹型關(guān)系的數(shù)據(jù),例如常見的類別表,即一個大類,下面有若干個子類,某些子類又有子類這樣的情況。當(dāng)類別不確定,用戶希望可以在任意類別下添加新的子類,或者刪除某個類別和其下的所有子類,而且預(yù)計以后其數(shù)量會逐步增長,此時我們就會考慮用一個數(shù)據(jù)表來保存這些數(shù)據(jù)。按照教科書上的教導(dǎo),第二類程序員大概會設(shè)計出類似這樣的數(shù)據(jù)表結(jié)構(gòu):

類別表_1(type_table_1)
名稱     類型    約束條件   說明
type_id      int        無重復(fù)     類別標(biāo)識,主鍵
type_name   char(50)    不允許為空   類型名稱,不允許重復(fù)
type_father   int         不允許為空   該類別的父類別標(biāo)識,如果是頂節(jié)點(diǎn)的話設(shè)定為某個唯一值

  這樣的設(shè)計短小精悍,完全滿足3nf,而且可以滿足用戶的所有要求。是不是這樣就行呢?答案是no!why?

  我們來估計一下用戶希望如何羅列出這個表的數(shù)據(jù)的。對用戶而言,他當(dāng)然期望按他所設(shè)定的層次關(guān)系一次羅列出所有的類別,例如這樣:
總類別
  類別1
    類別1.1
      類別1.1.1
    類別1.2
  類別2
    類別2.1
  類別3
    類別3.1
    類別3.2
  ……

  看看為了實(shí)現(xiàn)這樣的列表顯示(樹的先序遍歷),要對上面的表進(jìn)行多少次檢索?注意,盡管類別1.1.1可能是在類別3.2之后添加的記錄,答案仍然是n次。這樣的效率對于少量的數(shù)據(jù)沒什么影響,但是日后類型擴(kuò)充到數(shù)十條甚至上百條記錄后,單單列一次類型就要檢索數(shù)十次該表,整個程序的運(yùn)行效率就不敢恭維了?;蛟S第二類程序員會說,那我再建一個臨時數(shù)組或臨時表,專門保存類型表的先序遍歷結(jié)果,這樣只在第一次運(yùn)行時檢索數(shù)十次,再次羅列所有的類型關(guān)系時就直接讀那個臨時數(shù)組或臨時表就行了。其實(shí),用不著再去分配一塊新的內(nèi)存來保存這些數(shù)據(jù),只要對數(shù)據(jù)表進(jìn)行一定的擴(kuò)充,再對添加類型的數(shù)量進(jìn)行一下約束就行了,要完成上面的列表只需一次檢索就行了。下面是擴(kuò)充后的數(shù)據(jù)表結(jié)構(gòu):

類別表_2(type_table_2)
名稱     類型    約束條件                       說明
type_id      int        無重復(fù)                     類別標(biāo)識,主鍵
type_name   char(50)    不允許為空                   類型名稱,不允許重復(fù)
type_father   int         不允許為空                   該類別的父類別標(biāo)識,如果是頂節(jié)點(diǎn)的話設(shè)定為某個唯一值
type_layer    char(6)     限定3層,初始值為000000       類別的先序遍歷,主要為減少檢索數(shù)據(jù)庫的次數(shù)

  按照這樣的表結(jié)構(gòu),我們來看看上面例子記錄在表中的數(shù)據(jù)是怎樣的:

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
……

  現(xiàn)在按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
……

  現(xiàn)在列出的記錄順序正好是先序遍歷的結(jié)果。在控制顯示類別的層次時,只要對type_layer字段中的數(shù)值進(jìn)行判斷,每2位一組,如大于0則向右移2個空格。當(dāng)然,我這個例子中設(shè)定的限制條件是最多3層,每層最多可設(shè)99個子類別,只要按用戶的需求情況修改一下type_layer的長度和位數(shù),即可更改限制層數(shù)和子類別數(shù)。其實(shí),上面的設(shè)計不單單只在類別表中用到,網(wǎng)上某些可按樹型列表顯示的論壇程序大多采用類似的設(shè)計。

  或許有人認(rèn)為,type_table_2中的type_father字段是冗余數(shù)據(jù),可以除去。如果這樣,在插入、刪除某個類別的時候,就得對type_layer 的內(nèi)容進(jìn)行比較繁瑣的判定,所以我并沒有消去type_father字段,這也正符合數(shù)據(jù)庫設(shè)計中適當(dāng)保留冗余數(shù)據(jù)的來降低程序復(fù)雜度的原則,后面我會舉一個故意增加數(shù)據(jù)冗余的案例。

  
  二、商品信息表的設(shè)計
  假設(shè)你是一家百貨公司電腦部的開發(fā)人員,某天老板要求你為公司開發(fā)一套網(wǎng)上電子商務(wù)平臺,該百貨公司有數(shù)千種商品出售,不過目前僅打算先在網(wǎng)上銷售數(shù)十種方便運(yùn)輸?shù)纳唐罚?dāng)然,以后可能會陸續(xù)在該電子商務(wù)平臺上增加新的商品出售。現(xiàn)在開始進(jìn)行該平臺數(shù)據(jù)庫的商品信息表的設(shè)計。每種出售的商品都會有相同的屬性,如商品編號,商品名稱,商品所屬類別,相關(guān)信息,供貨廠商,內(nèi)含件數(shù),庫存,進(jìn)貨價,銷售價,優(yōu)惠價。你很快就設(shè)計出4個表:商品類型表(wares_type),供貨廠商表(wares_provider),商品信息表(wares_info):

商品類型表(wares_type)
名稱     類型    約束條件                       說明
type_id      int        無重復(fù)                     類別標(biāo)識,主鍵
type_name   char(50)    不允許為空                   類型名稱,不允許重復(fù)
type_father   int         不允許為空                   該類別的父類別標(biāo)識,如果是頂節(jié)點(diǎn)的話設(shè)定為某個唯一值
type_layer    char(6)     限定3層,初始值為000000       類別的先序遍歷,主要為減少檢索數(shù)據(jù)庫的次數(shù)

供貨廠商表(wares_provider)
名稱     類型    約束條件                       說明
provider_id   int        無重復(fù)                     供貨商標(biāo)識,主鍵
provider_name char(100)   不允許為空                   供貨商名稱

商品信息表(wares_info)
名稱      類型    約束條件                       說明
wares_id       int       無重復(fù)                       商品標(biāo)識,主鍵
wares_name     char(100)  不允許為空                     商品名稱
wares_type   int        不允許為空           商品類型標(biāo)識,和wares_type.type_id關(guān)聯(lián)
wares_info     char(200)  允許為空                       相關(guān)信息
provider       int        不允許為空                     供貨廠商標(biāo)識,和wares_provider.provider_id關(guān)聯(lián)
setnum         int        初始值為1                      內(nèi)含件數(shù),默認(rèn)為1
stock          int        初始值為0                      庫存,默認(rèn)為0
buy_price      money      不允許為空                     進(jìn)貨價
sell_price     money      不允許為空                     銷售價
discount       money      不允許為空                     優(yōu)惠價

  你拿著這3個表給老板檢查,老板希望能夠再添加一個商品圖片的字段,不過只有一部分商品有圖片。ok,你在商品信息表(wares_info)中增加了一個haspic的bool型字段,然后再建了一個新表——商品圖片表(wares_pic):

商品圖片表(wares_pic)
名稱      類型    約束條件                       說明
pic_id        int        無重復(fù)                       商品圖片標(biāo)識,主鍵
wares_id      int         不允許為空                     所屬商品標(biāo)識,和wares_info.wares_id關(guān)聯(lián)
pic_address  char(200)   不允許為空           圖片存放路徑

  程序開發(fā)完成后,完全滿足老板目前的要求,于是正式啟用。一段時間后,老板打算在這套平臺上推出新的商品銷售,其中,某類商品全部都需添加“長度”的屬性。第一輪折騰來了……當(dāng)然,你按照添加商品圖片表的老方法,在商品信息表(wares_info)中增加了一個haslength的bool型字段,又建了一個新表——商品長度表(wares_length):

商品長度表(wares_length)
名稱      類型    約束條件                       說明
length_id     int        無重復(fù)                       商品圖片標(biāo)識,主鍵
wares_id      int         不允許為空                     所屬商品標(biāo)識,和wares_info.wares_id關(guān)聯(lián)
length       char(20)    不允許為空           商品長度說明

  剛剛改完沒多久,老板又打算上一批新的商品,這次某類商品全部需要添加“寬度”的屬性。你咬了咬牙,又照方抓藥,添加了商品寬度表(wares_width)。又過了一段時間,老板新上的商品中有一些需要添加“高度”的屬性,你是不是開始覺得你所設(shè)計的數(shù)據(jù)庫按照這種方式增長下去,很快就能變成一個迷宮呢?那么,有沒有什么辦法遏制這種不可預(yù)見性,但卻類似重復(fù)的數(shù)據(jù)庫膨脹呢?我在閱讀《敏捷軟件開發(fā):原則、模式與實(shí)踐》中發(fā)現(xiàn)作者舉過類似的例子:7.3 “copy”程序。其中,我非常贊同敏捷軟件開發(fā)這個觀點(diǎn):在最初幾乎不進(jìn)行預(yù)先設(shè)計,但是一旦需求發(fā)生變化,此時作為一名追求卓越的程序員,應(yīng)該從頭審查整個架構(gòu)設(shè)計,在此次修改中設(shè)計出能夠滿足日后類似修改的系統(tǒng)架構(gòu)。下面是我在需要添加“長度”的屬性時所提供的修改方案:

  去掉商品信息表(wares_info)中的haspic字段,添加商品額外屬性表(wares_ex_property)和商品額外信息表(wares_ex_info)2個表來完成添加新屬性的功能。

商品額外屬性表(wares_ex_property)
名稱      類型    約束條件                       說明
ex_pid        int        無重復(fù)                       商品額外屬性標(biāo)識,主鍵
p_name        char(20)    不允許為空                     額外屬性名稱

商品額外信息表(wares_ex_info)
名稱        類型    約束條件                       說明
ex_iid          int        無重復(fù)                       商品額外信息標(biāo)識,主鍵
wares_id        int         不允許為空                     所屬商品標(biāo)識,和wares_info.wares_id關(guān)聯(lián)
property_id    int         不允許為空           商品額外屬性標(biāo)識,和wares_ex_property.ex_pid關(guān)聯(lián)
property_value  char(200)   不允許為空                     商品額外屬性值

  在商品額外屬性表(wares_ex_property)中添加2條記錄:
ex_pid            p_name
1                商品圖片
2                商品長度

  再在整個電子商務(wù)平臺的后臺管理功能中追加一項(xiàng)商品額外屬性管理的功能,以后添加新的商品時出現(xiàn)新的屬性,只需利用該功能往商品額外屬性表(wares_ex_property)中添加一條記錄即可。不要害怕變化,被第一顆子彈擊中并不是壞事,壞的是被相同軌道飛來的第二顆、第三顆子彈擊中。第一顆子彈來得越早,所受的傷越重,之后的抵抗力也越強(qiáng)8)(待續(xù))

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 肥西县| 武冈市| 鄂伦春自治旗| 钟祥市| 当雄县| 东阳市| 呼图壁县| 南投县| 佛山市| 弥渡县| 图木舒克市| 铜川市| 阳新县| 南靖县| 专栏| 岚皋县| 元江| SHOW| 景洪市| 龙陵县| 涡阳县| 扎赉特旗| 吉林省| 山阳县| 鄂托克前旗| 塘沽区| 吉木萨尔县| 郯城县| 临洮县| 唐山市| 青海省| 尼勒克县| 高雄市| 高邮市| 高密市| 承德市| 建阳市| 阿巴嘎旗| 蒙山县| 阳原县| 阳原县|