<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評(píng)論 - 101, 引用 - 0
    數(shù)據(jù)加載中……

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

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

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

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

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

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

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

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

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

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

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

      按照這樣的表結(jié)構(gòu),我們來(lái)看看上面例子記錄在表中的數(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的大小來(lái)檢索一下: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é)果。在控制顯示類別的層次時(shí),只要對(duì)type_layer字段中的數(shù)值進(jìn)行判斷,每2位一組,如大于0則向右移2個(gè)空格。當(dāng)然,我這個(gè)例子中設(shè)定的限制條件是最多3層,每層最多可設(shè)99個(gè)子類別,只要按用戶的需求情況修改一下type_layer的長(zhǎng)度和位數(shù),即可更改限制層數(shù)和子類別數(shù)。其實(shí),上面的設(shè)計(jì)不單單只在類別表中用到,網(wǎng)上某些可按樹(shù)型列表顯示的論壇程序大多采用類似的設(shè)計(jì)。

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

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

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

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

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

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

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

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

    商品長(zhǎng)度表(Wares_length)
    名稱    ? 類型    約束條件   ??????????????????? 說(shuō)明
    length_id???? int???  ?? 無(wú)重復(fù)  ???????????????????? 商品圖片標(biāo)識(shí),主鍵
    wares_id????? int???????? 不允許為空???????????????????? 所屬商品標(biāo)識(shí),和Wares_info.wares_id關(guān)聯(lián)
    length ????? char(20)??? 不允許為空           商品長(zhǎng)度說(shuō)明

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

      去掉商品信息表(Wares_info)中的haspic字段,添加商品額外屬性表(Wares_ex_property)和商品額外信息表(Wares_ex_info)2個(gè)表來(lái)完成添加新屬性的功能。

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

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

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

      再在整個(gè)電子商務(wù)平臺(tái)的后臺(tái)管理功能中追加一項(xiàng)商品額外屬性管理的功能,以后添加新的商品時(shí)出現(xiàn)新的屬性,只需利用該功能往商品額外屬性表(Wares_ex_property)中添加一條記錄即可。不要害怕變化,被第一顆子彈擊中并不是壞事,壞的是被相同軌道飛來(lái)的第二顆、第三顆子彈擊中。第一顆子彈來(lái)得越早,所受的傷越重,之后的抵抗力也越強(qiáng)8)
    三、多用戶及其權(quán)限管理的設(shè)計(jì)
      開(kāi)發(fā)數(shù)據(jù)庫(kù)管理類的軟件,不可能不考慮多用戶和用戶權(quán)限設(shè)置的問(wèn)題。盡管目前市面上的大、中型的后臺(tái)數(shù)據(jù)庫(kù)系統(tǒng)軟件都提供了多用戶,以及細(xì)至某個(gè)數(shù)據(jù)庫(kù)內(nèi)某張表的權(quán)限設(shè)置的功能,我個(gè)人建議:一套成熟的數(shù)據(jù)庫(kù)管理軟件,還是應(yīng)該自行設(shè)計(jì)用戶管理這塊功能,原因有二:
      1.那些大、中型后臺(tái)數(shù)據(jù)庫(kù)系統(tǒng)軟件所提供的多用戶及其權(quán)限設(shè)置都是針對(duì)數(shù)據(jù)庫(kù)的共有屬性,并不一定能完全滿足某些特例的需求;
      2.不要過(guò)多的依賴后臺(tái)數(shù)據(jù)庫(kù)系統(tǒng)軟件的某些特殊功能,多種大、中型后臺(tái)數(shù)據(jù)庫(kù)系統(tǒng)軟件之間并不完全兼容。否則一旦日后需要轉(zhuǎn)換數(shù)據(jù)庫(kù)平臺(tái)或后臺(tái)數(shù)據(jù)庫(kù)系統(tǒng)軟件版本升級(jí),之前的架構(gòu)設(shè)計(jì)很可能無(wú)法重用。

      下面看看如何自行設(shè)計(jì)一套比較靈活的多用戶管理模塊,即該數(shù)據(jù)庫(kù)管理軟件的系統(tǒng)管理員可以自行添加新用戶,修改已有用戶的權(quán)限,刪除已有用戶。首先,分析用戶需求,列出該數(shù)據(jù)庫(kù)管理軟件所有需要實(shí)現(xiàn)的功能;然后,根據(jù)一定的聯(lián)系對(duì)這些功能進(jìn)行分類,即把某類用戶需使用的功能歸為一類;最后開(kāi)始建表:
      
    功能表(Function_table)
    名稱     類型    約束條件   說(shuō)明
    f_id????????? int???  ?? 無(wú)重復(fù)  ?? 功能標(biāo)識(shí),主鍵
    f_name??????? char(20)??? 不允許為空?? 功能名稱,不允許重復(fù)
    f_desc??????? char(50)??? 允許為空???? 功能描述

    用戶組表(User_group)
    名稱     類型    約束條件   說(shuō)明
    group_id????? int???????? 無(wú)重復(fù)??????? 用戶組標(biāo)識(shí),主鍵
    group_name??? char(20)??? 不允許為空??? 用戶組名稱
    group_power?? char(100)?? 不允許為空??? 用戶組權(quán)限表,內(nèi)容為功能表f_id的集合

    用戶表(User_table)
    名稱     類型    約束條件   說(shuō)明
    user_id?????? int???????? 無(wú)重復(fù)??????? 用戶標(biāo)識(shí),主鍵
    user_name???? char(20)??? 無(wú)重復(fù)??????? 用戶名
    user_pwd????? char(20)??? 不允許為空??? 用戶密碼
    user_type???? int???????? 不允許為空??? 所屬用戶組標(biāo)識(shí),和User_group.group_id關(guān)聯(lián)

      采用這種用戶組的架構(gòu)設(shè)計(jì),當(dāng)需要添加新用戶時(shí),只需指定新用戶所屬的用戶組;當(dāng)以后系統(tǒng)需要添加新功能或?qū)εf有功能權(quán)限進(jìn)行修改時(shí),只用操作功能表和用戶組表的記錄,原有用戶的功能即可相應(yīng)隨之變化。當(dāng)然,這種架構(gòu)設(shè)計(jì)把數(shù)據(jù)庫(kù)管理軟件的功能判定移到了前臺(tái),使得前臺(tái)開(kāi)發(fā)相對(duì)復(fù)雜一些。但是,當(dāng)用戶數(shù)較大(10人以上),或日后軟件升級(jí)的概率較大時(shí),這個(gè)代價(jià)是值得的。


      四、簡(jiǎn)潔的批量m:n設(shè)計(jì)
      碰到m:n的關(guān)系,一般都是建立3個(gè)表,m一個(gè),n一個(gè),m:n一個(gè)。但是,m:n有時(shí)會(huì)遇到批量處理的情況,例如到圖書(shū)館借書(shū),一般都是允許用戶同時(shí)借閱n本書(shū),如果要求按批查詢借閱記錄,即列出某個(gè)用戶某次借閱的所有書(shū)籍,該如何設(shè)計(jì)呢?讓我們建好必須的3個(gè)表先:

    書(shū)籍表(Book_table)
    名稱     類型    約束條件   說(shuō)明
    book_id?????? int???????? 無(wú)重復(fù)??????? 書(shū)籍標(biāo)識(shí),主鍵
    book_no?????? char(20)??? 無(wú)重復(fù)??????? 書(shū)籍編號(hào)
    book_name???? char(100)?? 不允許為空??? 書(shū)籍名稱
    ……

    借閱用戶表(Renter_table)
    名稱     類型    約束條件   說(shuō)明
    renter_id???? int???????? 無(wú)重復(fù)??????? 用戶標(biāo)識(shí),主鍵
    renter_name?? char(20)??? 不允許為空??? 用戶姓名
    ……

    借閱記錄表(Rent_log)
    名稱     類型    約束條件   說(shuō)明
    rent_id?????? int???????? 無(wú)重復(fù)??????? 借閱記錄標(biāo)識(shí),主鍵
    r_id????????? int???????? 不允許為空??? 用戶標(biāo)識(shí),和Renter_table.renter_id關(guān)聯(lián)
    b_id????????? int???????? 不允許為空??? 書(shū)籍標(biāo)識(shí),和Book_table.book_id關(guān)聯(lián)
    rent_date???? datetime??? 不允許為空??? 借閱時(shí)間
    ……

      為了實(shí)現(xiàn)按批查詢借閱記錄,我們可以再建一個(gè)表來(lái)保存批量借閱的信息,例如:

    批量借閱表(Batch_rent)
    名稱     類型    約束條件   說(shuō)明
    batch_id????? int???????? 無(wú)重復(fù)??????? 批量借閱標(biāo)識(shí),主鍵
    batch_no????? int???????? 不允許為空??? 批量借閱編號(hào),同一批借閱的batch_no相同
    rent_id?????? int???????? 不允許為空??? 借閱記錄標(biāo)識(shí),和Rent_log.rent_id關(guān)聯(lián)
    batch_date??? datetime??? 不允許為空??? 批量借閱時(shí)間

      這樣的設(shè)計(jì)好嗎?我們來(lái)看看為了列出某個(gè)用戶某次借閱的所有書(shū)籍,需要如何查詢?首先檢索批量借閱表(Batch_rent),把符合條件的的所有記錄的rent_id字段的數(shù)據(jù)保存起來(lái),再用這些數(shù)據(jù)作為查詢條件帶入到借閱記錄表(Rent_log)中去查詢。那么,有沒(méi)有什么辦法改進(jìn)呢?下面給出一種簡(jiǎn)潔的批量設(shè)計(jì)方案,不需添加新表,只需修改一下借閱記錄表(Rent_log)即可。修改后的記錄表(Rent_log)如下:

    借閱記錄表(Rent_log)
    名稱     類型    約束條件   說(shuō)明
    rent_id?????? int???????? 無(wú)重復(fù)??????? 借閱記錄標(biāo)識(shí),主鍵
    r_id????????? int???????? 不允許為空??? 用戶標(biāo)識(shí),和Renter_table.renter_id關(guān)聯(lián)
    b_id????????? int???????? 不允許為空??? 書(shū)籍標(biāo)識(shí),和Book_table.book_id關(guān)聯(lián)
    batch_no????? int???????? 不允許為空??? 批量借閱編號(hào),同一批借閱的batch_no相同
    rent_date???? datetime??? 不允許為空??? 借閱時(shí)間
    ……

      其中,同一次借閱的batch_no和該批第一條入庫(kù)的rent_id相同。舉例:假設(shè)當(dāng)前最大rent_id是64,接著某用戶一次借閱了3本書(shū),則批量插入的3條借閱記錄的batch_no都是65。之后另外一個(gè)用戶租了一套碟,再插入出租記錄的rent_id是68。采用這種設(shè)計(jì),查詢批量借閱的信息時(shí),只需使用一條標(biāo)準(zhǔn)T_SQL的嵌套查詢即可。當(dāng)然,這種設(shè)計(jì)不符合3NF,但是和上面標(biāo)準(zhǔn)的3NF設(shè)計(jì)比起來(lái),哪一種更好呢?答案就不用我說(shuō)了吧。


      五、冗余數(shù)據(jù)的取舍
      上篇的“樹(shù)型關(guān)系的數(shù)據(jù)表”中保留了一個(gè)冗余字段,這里的例子更進(jìn)一步——添加了一個(gè)冗余表。先看看例子:我原先所在的公司為了解決員工的工作餐,和附近的一家小餐館聯(lián)系,每天吃飯記賬,費(fèi)用按人數(shù)平攤,月底由公司現(xiàn)金結(jié)算,每個(gè)人每個(gè)月的工作餐費(fèi)從工資中扣除。當(dāng)然,每天吃飯的人員和人數(shù)都不是固定的,而且,由于每頓工作餐的所點(diǎn)的菜色不同,每頓的花費(fèi)也不相同。例如,星期一中餐5人花費(fèi)40元,晚餐2人花費(fèi)20,星期二中餐6人花費(fèi)36元,晚餐3人花費(fèi)18元。為了方便計(jì)算每個(gè)人每個(gè)月的工作餐費(fèi),我寫(xiě)了一個(gè)簡(jiǎn)陋的就餐記賬管理程序,數(shù)據(jù)庫(kù)里有3個(gè)表:

    員工表(Clerk_table)
    名稱     類型    約束條件   說(shuō)明
    clerk_id????? int???????? 無(wú)重復(fù)??????? 員工標(biāo)識(shí),主鍵
    clerk_name??? char(10)??? 不允許為空??? 員工姓名

    每餐總表(Eatdata1)
    名稱     類型    約束條件   說(shuō)明
    totle_id????? int???????? 無(wú)重復(fù)??????? 每餐總表標(biāo)識(shí),主鍵
    persons?????? char(100)?? 不允許為空??? 就餐員工的員工標(biāo)識(shí)集合
    eat_date????? datetime??? 不允許為空??? 就餐日期
    eat_type????? char(1)???? 不允許為空??? 就餐類型,用來(lái)區(qū)分中、晚餐
    totle_price?? money?????? 不允許為空??? 每餐總花費(fèi)
    persons_num?? int???????? 不允許為空??? 就餐人數(shù)

    就餐計(jì)費(fèi)細(xì)表(Eatdata2)
    名稱     類型    約束條件   說(shuō)明
    id??????????? int???????? 無(wú)重復(fù)??????? 就餐計(jì)費(fèi)細(xì)表標(biāo)識(shí),主鍵
    t_id????????? int???????? 不允許為空??? 每餐總表標(biāo)識(shí),和Eatdata1.totle_id關(guān)聯(lián)
    c_id????????? int???????? 不允許為空??? 員工標(biāo)識(shí)標(biāo)識(shí),和Clerk_table.clerk_id關(guān)聯(lián)
    price???????? money?????? 不允許為空??? 每人每餐花費(fèi)

      其中,就餐計(jì)費(fèi)細(xì)表(Eatdata2)的記錄就是把每餐總表(Eatdata1)的一條記錄按就餐員工平攤拆開(kāi),是個(gè)不折不扣的冗余表。當(dāng)然,也可以把每餐總表(Eatdata1)的部分字段合并到就餐計(jì)費(fèi)細(xì)表(Eatdata2)中,這樣每餐總表(Eatdata1)就成了冗余表,不過(guò)這樣所設(shè)計(jì)出來(lái)的就餐計(jì)費(fèi)細(xì)表重復(fù)數(shù)據(jù)更多,相比來(lái)說(shuō)還是上面的方案好些。但是,就是就餐計(jì)費(fèi)細(xì)表(Eatdata2)這個(gè)冗余表,在做每月每人餐費(fèi)統(tǒng)計(jì)的時(shí)候,大大簡(jiǎn)化了編程的復(fù)雜度,只用類似這么一條查詢語(yǔ)句即可統(tǒng)計(jì)出每人每月的寄餐次數(shù)和餐費(fèi)總帳:

    SELECT clerk_name AS personname,COUNT(c_id) as eattimes,SUM(price) AS ptprice FROM Eatdata2 JOIN Clerk_tabsle ON (c_id=clerk_id) JOIN eatdata1 ON (totleid=tid) WHERE eat_date>=CONVERT(datetime,'"&the_date&"') AND eat_date<DATEADD(month,1,CONVERT(datetime,'"&the_date&"')) GROUP BY c_id

      想象一下,如果不用這個(gè)冗余表,每次統(tǒng)計(jì)每人每月的餐費(fèi)總帳時(shí)會(huì)多麻煩,程序效率也夠嗆。那么,到底什么時(shí)候可以增加一定的冗余數(shù)據(jù)呢?我認(rèn)為有2個(gè)原則:

      1、用戶的整體需求。當(dāng)用戶更多的關(guān)注于,對(duì)數(shù)據(jù)庫(kù)的規(guī)范記錄按一定的算法進(jìn)行處理后,再列出的數(shù)據(jù)。如果該算法可以直接利用后臺(tái)數(shù)據(jù)庫(kù)系統(tǒng)的內(nèi)嵌函數(shù)來(lái)完成,此時(shí)可以適當(dāng)?shù)脑黾尤哂嘧侄危踔寥哂啾韥?lái)保存這些經(jīng)過(guò)算法處理后的數(shù)據(jù)。要知道,對(duì)于大批量數(shù)據(jù)的查詢,修改或刪除,后臺(tái)數(shù)據(jù)庫(kù)系統(tǒng)的效率遠(yuǎn)遠(yuǎn)高于我們自己編寫(xiě)的代碼。
      2、簡(jiǎn)化開(kāi)發(fā)的復(fù)雜度。現(xiàn)代軟件開(kāi)發(fā),實(shí)現(xiàn)同樣的功能,方法有很多。盡管不必要求程序員精通絕大部分的開(kāi)發(fā)工具和平臺(tái),但是還是需要了解哪種方法搭配哪種開(kāi)發(fā)工具的程序更簡(jiǎn)潔,效率更高一些。冗余數(shù)據(jù)的本質(zhì)就是用空間換時(shí)間,尤其是目前硬件的發(fā)展遠(yuǎn)遠(yuǎn)高于軟件,所以適當(dāng)?shù)娜哂嗍强梢越邮艿摹2贿^(guò)我還是在最后再?gòu)?qiáng)調(diào)一下:不要過(guò)多的依賴平臺(tái)和開(kāi)發(fā)工具的特性來(lái)簡(jiǎn)化開(kāi)發(fā),這個(gè)度要是沒(méi)把握好的話,后期維護(hù)升級(jí)會(huì)栽大跟頭的。

    from: http://www.knowsky.com/4937.html

    posted on 2006-09-29 17:39 weidagang2046 閱讀(208) 評(píng)論(0)  編輯  收藏 所屬分類: Database

    主站蜘蛛池模板: www一区二区www免费| 亚洲精品无码专区2| 免费久久人人爽人人爽av| 亚洲国产精品久久久久秋霞小| 亚洲av日韩av无码黑人| 亚洲精品无码久久毛片| 国产真人无遮挡作爱免费视频 | 免费一级毛片一级毛片aa| 18禁止观看免费私人影院| 大地影院MV在线观看视频免费 | 免费一级毛片免费播放| 妞干网免费视频观看| 无码精品A∨在线观看免费| 无码午夜成人1000部免费视频| 两个人看的www视频免费完整版| 色噜噜的亚洲男人的天堂| 亚洲综合精品伊人久久| 亚洲激情黄色小说| 亚洲黄色网址大全| 亚洲麻豆精品果冻传媒| 亚洲人成影院在线| 午夜亚洲www湿好大| 亚洲Av无码精品色午夜| 国产成人A人亚洲精品无码| 亚洲乱亚洲乱妇无码麻豆| 国外亚洲成AV人片在线观看| 亚洲人成人无码网www国产| 亚洲成AV人网址| 亚洲国产精品自在拍在线播放| 国产精品国产免费无码专区不卡| 好吊妞视频免费视频| 韩国免费三片在线视频| 成人片黄网站色大片免费| 拨牐拨牐x8免费| 永久黄网站色视频免费直播| 免费观看的毛片手机视频| 国产免费人成视频在线观看 | 色老头综合免费视频| 成人久久久观看免费毛片| 一个人看的在线免费视频| 花蝴蝶免费视频在线观看高清版 |