黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

數(shù)據(jù)庫實戰(zhàn)經(jīng)驗之淺談數(shù)據(jù)庫的設(shè)計技巧

系統(tǒng) 2127 0

注:如本網(wǎng)轉(zhuǎn)載的教程文章涉及版權(quán)等問題,請作者與我聯(lián)系,我將在第一時間刪除。謝謝! 聯(lián)絡(luò)郵箱(Contact E-mail): sfzheng@foxmail.com

· 經(jīng)典實用SQL語句大全總結(jié)
· SQL的簡單查詢實例教程
關(guān)鍵詞:數(shù)據(jù)庫實戰(zhàn)經(jīng)驗之淺談數(shù)據(jù)庫的設(shè)計技巧

  說到數(shù)據(jù)庫,我認(rèn)為不能不先談數(shù)據(jù)結(jié)構(gòu)。1996年,在我初入大學(xué)學(xué)習(xí)計算機編程時,當(dāng)時的老師就告訴我們說:計算機程序=數(shù)據(jù)結(jié)構(gòu)+算法。盡管現(xiàn)在的程序開發(fā)已由面向過程為主逐步過渡到面向?qū)ο鬄橹鳎疫€是深深贊同8年前老師的告訴我們的公式:計算機程序=數(shù)據(jù)結(jié)構(gòu)+算法。面向?qū)ο蟮某绦蜷_發(fā),要做的第一件事就是,先分析整個程序中需處理的數(shù)據(jù),從中提取出抽象模板,以這個抽象模板設(shè)計類,再在其中逐步添加處理其數(shù)據(jù)的函數(shù)(即算法),最后,再給類中的數(shù)據(jù)成員和函數(shù)劃分訪問權(quán)限,從而實現(xiàn)封裝。 ?
  數(shù)據(jù)庫的最初雛形據(jù)說源自美國一個奶牛場的記賬薄(紙質(zhì)的,由此可見,數(shù)據(jù)庫并不一定是存儲在電腦里的數(shù)據(jù)^_^),里面記錄的是該奶牛場的收支賬目,程序員在將其整理、錄入到電腦中時從中受到啟發(fā)。當(dāng)按照規(guī)定好的數(shù)據(jù)結(jié)構(gòu)所采集到的數(shù)據(jù)量大到一定程度后,出于程序執(zhí)行效率的考慮,程序員將其中的檢索、更新維護等功能分離出來,做成單獨調(diào)用的模塊,這個模塊后來就慢慢發(fā)展、演變成現(xiàn)在我們所接觸到的數(shù)據(jù)庫管理系統(tǒng)(DBMS)——程序開發(fā)中的一個重要分支。
  下面進入正題,首先按我個人所接觸過的程序給數(shù)據(jù)庫設(shè)計人員的功底分一下類:

  1、沒有系統(tǒng)學(xué)習(xí)過數(shù)據(jù)結(jié)構(gòu)的程序員。這類程序員的作品往往只是他們的即興玩具,他們往往習(xí)慣只設(shè)計有限的幾個表,實現(xiàn)某類功能的數(shù)據(jù)全部塞在一個表中,各表之間幾乎毫無關(guān)聯(lián)。網(wǎng)上不少的免費管理軟件都是這樣的東西,當(dāng)程序功能有限,數(shù)據(jù)量不多的時候,其程序運行起來沒有什么問題,但是如果用其管理比較重要的數(shù)據(jù),風(fēng)險性非常大。

  2、系統(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ù)庫表差不多得進行大換血。

  3、第二類程序員,在經(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è)計后,第三類程序員中堅持下來沒有轉(zhuǎn)行,而是希望從中找出“偷懶”竅門的有心人會慢慢覺悟,從而完成量變到質(zhì)變的轉(zhuǎn)換。他們所設(shè)計的數(shù)據(jù)庫表結(jié)構(gòu)有一定的遠見,能夠預(yù)測到未來功能升級所需要的數(shù)據(jù),從而預(yù)先留下伏筆。這類程序員目前大多晉級成數(shù)據(jù)挖掘方面的高級軟件開發(fā)人員。

  5、第三類程序員或第四類程序員,在對現(xiàn)有的各家數(shù)據(jù)庫管理系統(tǒng)的原理和開發(fā)都有一定的鉆研后,要么在其基礎(chǔ)上進行二次開發(fā),要么自行開發(fā)一套有自主版權(quán)的通用數(shù)據(jù)庫管理系統(tǒng)。
  我個人正處于第三類的末期,所以下面所列出的一些設(shè)計技巧只適合第二類和部分第三類數(shù)據(jù)庫設(shè)計人員。同時,由于我很少碰到有興趣在這方面深鉆下去的同行,所以文中難免出現(xiàn)錯誤和遺漏,在此先行聲明,歡迎大家指正,不要藏私哦8)
一、樹型關(guān)系的數(shù)據(jù)表

  不少程序員在進行數(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é)點的話設(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   ……
  看看為了實現(xiàn)這樣的列表顯示(樹的先序遍歷),要對上面的表進行多少次檢索?注意,盡管類別1.1.1可能是在類別3.2之后添加的記錄,答案仍然是N次。這樣的效率對于少量的數(shù)據(jù)沒什么影響,但是日后類型擴充到數(shù)十條甚至上百條記錄后,單單列一次類型就要檢索數(shù)十次該表,整個程序的運行效率就不敢恭維了。或許第二類程序員會說,那我再建一個臨時數(shù)組或臨時表,專門保存類型表的先序遍歷結(jié)果,這樣只在第一次運行時檢索數(shù)十次,再次羅列所有的類型關(guān)系時就直接讀那個臨時數(shù)組或臨時表就行了。其實,用不著再去分配一塊新的內(nèi)存來保存這些數(shù)據(jù),只要對數(shù)據(jù)表進行一定的擴充,再對添加類型的數(shù)量進行一下約束就行了,要完成上面的列表只需一次檢索就行了。下面是擴充后的數(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é)點的話設(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ù)值進行判斷,每2位一組,如大于0則向右移2個空格。當(dāng)然,我這個例子中設(shè)定的限制條件是最多3層,每層最多可設(shè)99個子類別,只要按用戶的需求情況修改一下type_layer的長度和位數(shù),即可更改限制層數(shù)和子類別數(shù)。其實,上面的設(shè)計不單單只在類別表中用到,網(wǎng)上某些可按樹型列表顯示的論壇程序大多采用類似的設(shè)計。
  或許有人認(rèn)為,Type_table_2中的type_father字段是冗余數(shù)據(jù),可以除去。如果這樣,在插入、刪除某個類別的時候,就得對type_layer 的內(nèi)容進行比較繁瑣的判定,所以我并沒有消去type_father字段,這也正符合數(shù)據(jù)庫設(shè)計中適當(dāng)保留冗余數(shù)據(jù)的來降低程序復(fù)雜度的原則,后面我會舉一個故意增加數(shù)據(jù)冗余的案例。
二、商品信息表的設(shè)計

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

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ā):原則、模式與實踐》中發(fā)現(xiàn)作者舉過類似的例子:7.3 “Copy”程序。其中,我非常贊同敏捷軟件開發(fā)這個觀點:在最初幾乎不進行預(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àn)新的屬性,只需利用該功能往商品額外屬性表(Wares_ex_property)中添加一條記錄即可。不要害怕變化,被第一顆子彈擊中并不是壞事,壞的是被相同軌道飛來的第二顆、第三顆子彈擊中。第一顆子彈來得越早,所受的傷越重,之后的抵抗力也越強8)(待續(xù))
三、多用戶及其權(quán)限管理的設(shè)計

  開發(fā)數(shù)據(jù)庫管理類的軟件,不可能不考慮多用戶和用戶權(quán)限設(shè)置的問題。盡管目前市面上的大、中型的后臺數(shù)據(jù)庫系統(tǒng)軟件都提供了多用戶,以及細(xì)至某個數(shù)據(jù)庫內(nèi)某張表的權(quán)限設(shè)置的功能,我個人建議:一套成熟的數(shù)據(jù)庫管理軟件,還是應(yīng)該自行設(shè)計用戶管理這塊功能,原因有二:   1.那些大、中型后臺數(shù)據(jù)庫系統(tǒng)軟件所提供的多用戶及其權(quán)限設(shè)置都是針對數(shù)據(jù)庫的共有屬性,并不一定能完全滿足某些特例的需求;   2.不要過多的依賴后臺數(shù)據(jù)庫系統(tǒng)軟件的某些特殊功能,多種大、中型后臺數(shù)據(jù)庫系統(tǒng)軟件之間并不完全兼容。否則一旦日后需要轉(zhuǎn)換數(shù)據(jù)庫平臺或后臺數(shù)據(jù)庫系統(tǒng)軟件版本升級,之前的架構(gòu)設(shè)計很可能無法重用。
  下面看看如何自行設(shè)計一套比較靈活的多用戶管理模塊,即該數(shù)據(jù)庫管理軟件的系統(tǒng)管理員可以自行添加新用戶,修改已有用戶的權(quán)限,刪除已有用戶。首先,分析用戶需求,列出該數(shù)據(jù)庫管理軟件所有需要實現(xiàn)的功能;然后,根據(jù)一定的聯(lián)系對這些功能進行分類,即把某類用戶需使用的功能歸為一類;最后開始建表:

功能表(Function_table) 名稱     類型    約束條件   說明 f_id ? ? int ?   ? 無重復(fù)   ? 功能標(biāo)識,主鍵 f_name ? char(20) ? 不允許為空 ? 功能名稱,不允許重復(fù) f_desc ? char(50) ? 允許為空 ? 功能描述
用戶組表(User_group) 名稱     類型    約束條件   說明 group_id ? int ? ? 無重復(fù) ? 用戶組標(biāo)識,主鍵 group_name ? char(20) ? 不允許為空 ? 用戶組名稱 group_power ? char(100) ? 不允許為空 ? 用戶組權(quán)限表,內(nèi)容為功能表f_id的集合
用戶表(User_table) 名稱     類型    約束條件   說明 user_id ? int ? ? 無重復(fù) ? 用戶標(biāo)識,主鍵 user_name ? char(20) ? 無重復(fù) ? 用戶名 user_pwd ? char(20) ? 不允許為空 ? 用戶密碼 user_type ? int ? ? 不允許為空 ? 所屬用戶組標(biāo)識,和User_group.group_id關(guān)聯(lián)

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

四、簡潔的批量m:n設(shè)計

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

書籍表(Book_table)

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

借閱用戶表(Renter_table)

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

借閱記錄表(Rent_log)

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

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

批量借閱表(Batch_rent)

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

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

借閱記錄表(Rent_log)

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

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

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

員工表(Clerk_table)

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

每餐總表(Eatdata1)

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

就餐計費細(xì)表(Eatdata2)

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

  其中,就餐計費細(xì)表(Eatdata2)的記錄就是把每餐總表(Eatdata1)的一條記錄按就餐員工平攤拆開,是個不折不扣的冗余表。當(dāng)然,也可以把每餐總表(Eatdata1)的部分字段合并到就餐計費細(xì)表(Eatdata2)中,這樣每餐總表(Eatdata1)就成了冗余表,不過這樣所設(shè)計出來的就餐計費細(xì)表重復(fù)數(shù)據(jù)更多,相比來說還是上面的方案好些。但是,就是就餐計費細(xì)表(Eatdata2)這個冗余表,在做每月每人餐費統(tǒng)計的時候,大大簡化了編程的復(fù)雜度,只用類似這么一條查詢語句即可統(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ù)呢?我認(rèn)為有2個原則:
  1、用戶的整體需求。當(dāng)用戶更多的關(guān)注于,對數(shù)據(jù)庫的規(guī)范記錄按一定的算法進行處理后,再列出的數(shù)據(jù)。如果該算法可以直接利用后臺數(shù)據(jù)庫系統(tǒng)的內(nèi)嵌函數(shù)來完成,此時可以適當(dāng)?shù)脑黾尤哂嘧侄危踔寥哂啾韥肀4孢@些經(jīng)過算法處理后的數(shù)據(jù)。要知道,對于大批量數(shù)據(jù)的查詢,修改或刪除,后臺數(shù)據(jù)庫系統(tǒng)的效率遠遠高于我們自己編寫的代碼。

  2、簡化開發(fā)的復(fù)雜度。現(xiàn)代軟件開發(fā),實現(xiàn)同樣的功能,方法有很多。盡管不必要求程序員精通絕大部分的開發(fā)工具和平臺,但是還是需要了解哪種方法搭配哪種開發(fā)工具的程序更簡潔,效率更高一些。冗余數(shù)據(jù)的本質(zhì)就是用空間換時間,尤其是目前硬件的發(fā)展遠遠高于軟件,所以適當(dāng)?shù)娜哂嗍强梢越邮艿摹2贿^我還是在最后再強調(diào)一下:不要過多的依賴平臺和開發(fā)工具的特性來簡化開發(fā),這個度要是沒把握好的話,后期維護升級會栽大跟頭的。

數(shù)據(jù)庫實戰(zhàn)經(jīng)驗之淺談數(shù)據(jù)庫的設(shè)計技巧


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論