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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          三、多用戶及其權限管理設計
            開發(fā)數(shù)據(jù)庫管理類的軟件,不可能不考慮多用戶和用戶權限設置的問題。盡管目前市面上的大、中型的后臺數(shù)據(jù)庫系統(tǒng)軟件都提供了多用戶,以及細至某個數(shù)據(jù)庫內(nèi)某張表的權限設置的功能,我個人建議:一套成熟的數(shù)據(jù)庫管理軟件,還是應該自行設計用戶管理這塊功能,原因有二:
            1.那些大、中型后臺數(shù)據(jù)庫系統(tǒng)軟件所提供的多用戶及其權限設置都是針對數(shù)據(jù)庫的共有屬性,并不一定能完全滿足某些特例的需求;
            2.不要過多的依賴后臺數(shù)據(jù)庫系統(tǒng)軟件的某些特殊功能,多種大、中型后臺數(shù)據(jù)庫系統(tǒng)軟件之間并不完全兼容。否則一旦日后需要轉(zhuǎn)換數(shù)據(jù)庫平臺或后臺數(shù)據(jù)庫系統(tǒng)軟件版本升級,之前的架構設計很可能無法重用。

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

          用戶組表(User_group)
          名稱     類型    約束條件   說明
          group_id                 int                    無重復        用戶組標識,主鍵
          group_name         char(20)          不允許為空    用戶組名稱
          group_power        char(100)       不允許為空    用戶組權限表,內(nèi)容為功能表f_id的集合

          用戶表(User_table)
          名稱     類型    約束條件            說明
          user_id                   int                    無重復                用戶標識,主鍵
          user_name           char(20)             無重復                     用戶名
          user_pwd            char(20)          不允許為空              用戶密碼
          user_type               int                不允許為空          所屬用戶組標識,和User_group.group_id關聯(lián)

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


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

          書籍表(Book_table)
          名稱     類型    約束條件   說明
          book_id                 int                    無重復          書籍標識,主鍵
          book_no             char(20)             無重復           書籍編號
          book_name         char(100)         不允許為空     書籍名稱
          ……

          借閱用戶表(Renter_table)
          名稱     類型    約束條件   說明
          renter_id                int                     無重復        用戶標識,主鍵
          renter_name       char(20)           不允許為空       用戶姓名
          ……

          借閱記錄表(Rent_log)
          名稱     類型    約束條件     說明
          rent_id                   int                      無重復          借閱記錄標識,主鍵
          r_id                       int                  不允許為空    用戶標識,和Renter_table.renter_id關聯(lián)
          b_id                      int                  不允許為空    書籍標識,和Book_table.book_id關聯(lián)
          rent_date           datetime            不允許為空           借閱時間
          ……

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

          批量借閱表(Batch_rent)
          名稱     類型    約束條件      說明
          batch_id                 int                    無重復          批量借閱標識,主鍵
          batch_no                int                  不允許為空    批量借閱編號,同一批借閱的batch_no相同
          rent_id                   int                  不允許為空    借閱記錄標識,和Rent_log.rent_id關聯(lián)
          batch_date          datetime            不允許為空    批量借閱時間

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

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

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


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

          員工表(Clerk_table)
          名稱     類型    約束條件   說明
          clerk_id                 int                      無重復          員工標識,主鍵
          clerk_name         char(10)             不允許為空    員工姓名

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

          就餐計費細表(Eatdata2)
          名稱     類型    約束條件   說明
          id                           int                    無重復          就餐計費細表標識,主鍵
          t_id                        int                  不允許為空    每餐總表標識,和Eatdata1.totle_id關聯(lián)
          c_id                       int                  不允許為空    員工標識標識,和Clerk_table.clerk_id關聯(lián)
          price                   money              不允許為空     每人每餐花費

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

          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

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

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

          posted on 2007-10-18 14:08 譚明 閱讀(223) 評論(0)  編輯  收藏 所屬分類: 數(shù)據(jù)庫設計

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 桂阳县| 泗水县| 定襄县| 民县| 广德县| 中西区| 庐江县| 阳山县| 海伦市| 建宁县| 沂水县| 商河县| 罗定市| 招远市| 乐昌市| 平度市| 屏南县| 蛟河市| 麦盖提县| 砚山县| 南川市| 响水县| 武清区| 莎车县| 平塘县| 高安市| 枣强县| 海口市| 肇州县| 沙坪坝区| 灵武市| 高碑店市| 澄江县| 益阳市| 澜沧| 临沭县| 双流县| 大兴区| 永康市| 会同县| 郓城县|