淺談資料庫設計技巧(下)(轉)

阿笨net發表於2012-12-08
三、多使用者及其許可權管理的設計
  開發資料庫管理類的軟體,不可能不考慮多使用者和使用者許可權設定的問題。儘管目前市面上的大、中型的後臺資料庫系統軟體都提供了多使用者,以及細至某個資料庫內某張表的許可權設定的功能,我個人建議:一套成熟的資料庫管理軟體,還是應該自行設計使用者管理這塊功能,原因有二:
  1.那些大、中型後臺資料庫系統軟體所提供的多使用者及其許可權設定都是針對資料庫的共有屬性,並不一定能完全滿足某些特例的需求;
  2.不要過多的依賴後臺資料庫系統軟體的某些特殊功能,多種大、中型後臺資料庫系統軟體之間並不完全相容。否則一旦日後需要轉換資料庫平臺或後臺資料庫系統軟體版本升級,之前的架構設計很可能無法重用。
   下面看看如何自行設計一套比較靈活的多使用者管理模組,即該資料庫管理軟體的系統管理員可以自行新增新使用者,修改已有使用者的許可權,刪除已有使用者。首先,分析使用者需求,列出該資料庫管理軟體所有需要實現的功能;然後,根據一定的聯絡對這些功能進行分類,即把某類使用者需使用的功能歸為一類;最後開始建表:
  
功能表(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) 不允許為空 使用者組許可權表,內容為功能表f_id的集合
 
使用者表(User_table)
名稱     型別    約束條件   說明
user_id int 無重複 使用者標識,主鍵
user_name char(20) 無重複 使用者名稱
user_pwd char(20) 不允許為空 使用者密碼
user_type int 不允許為空 所屬使用者組標識,和User_group.group_id關聯
 
  採用這種使用者組的架構設計,當需要新增新使用者時,只需指定新使用者所屬的使用者組;當以後系統需要新增新功能或對舊有功能許可權進行修改時,只用操作功能表和使用者組表的記錄,原有使用者的功能即可相應隨之變化。當然,這種架構設計把資料庫管理軟體的功能判定移到了前臺,使得前臺開發相對複雜一些。但是,當使用者數較大(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關聯
b_id int 不允許為空 書籍標識,和Book_table.book_id關聯
rent_date datetime 不允許為空 借閱時間
……
 
  為了實現按批查詢借閱記錄,我們可以再建一個表來儲存批量借閱的資訊,例如:
 
批量借閱表(Batch_rent)
名稱     型別    約束條件   說明
batch_id int 無重複 批量借閱標識,主鍵
batch_no int 不允許為空 批量借閱編號,同一批借閱的batch_no相同
rent_id int 不允許為空 借閱記錄標識,和Rent_log.rent_id關聯
batch_date datetime 不允許為空 批量借閱時間
 
  這樣的設計好嗎?我們來看看為了列出某個使用者某次借閱的所有書籍,需要如何查詢?首先檢索批量借閱表(Batch_rent),把符合條件的的所有記錄的rent_id欄位的資料儲存起來,再用這些資料作為查詢條件帶入到借閱記錄表(Rent_log)中去查詢。那麼,有沒有什麼辦法改進呢?下面給出一種簡潔的批量設計方案,不需新增新表,只需修改一下借閱記錄表(Rent_log)即可。修改後的記錄表(Rent_log)如下:
 
借閱記錄表(Rent_log)
名稱     型別    約束條件   說明
rent_id int 無重複 借閱記錄標識,主鍵
r_id int 不允許為空 使用者標識,和Renter_table.renter_id關聯
b_id int 不允許為空 書籍標識,和Book_table.book_id關聯
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設計比起來,哪一種更好呢?答案就不用我說了吧。
 

  五、冗餘資料的取捨
  上篇的“樹型關係的資料表”中保留了一個冗餘欄位,這裡的例子更進一步——新增了一個冗餘表。先看看例子:我原先所在的公司為了解決員工的工作餐,和附近的一家小餐館聯絡,每天吃飯記賬,費用按人數平攤,月底由公司現金結算,每個人每個月的工作餐費從工資中扣除。當然,每天吃飯的人員和人數都不是固定的,而且,由於每頓工作餐的所點的菜色不同,每頓的花費也不相同。例如,星期一中餐5人花費40元,晚餐2人花費20,星期二中餐6人花費36元,晚餐3人花費18元。為了方便計算每個人每個月的工作餐費,我寫了一個簡陋的就餐記賬管理程式,資料庫裡有3個表:
 
員工表(Clerk_table)
名稱     型別    約束條件   說明
clerk_id int 無重複 員工標識,主鍵
clerk_name char(10) 不允許為空 員工姓名
 
每餐總表(Eatdata1)
名稱     型別    約束條件   說明
totle_id int 無重複 每餐總表標識,主鍵
persons char(100) 不允許為空 就餐員工的員工標識集合
eat_date datetime 不允許為空 就餐日期
eat_type char(1) 不允許為空 就餐型別,用來區分中、晚餐
totle_price money 不允許為空 每餐總花費
persons_num int 不允許為空 就餐人數
 
就餐計費細表(Eatdata2)
名稱     型別    約束條件   說明
id int 無重複 就餐計費細表標識,主鍵
t_id int 不允許為空 每餐總表標識,和Eatdata1.totle_id關聯
c_id int 不允許為空 員工標識標識,和Clerk_table.clerk_id關聯
price money 不允許為空 每人每餐花費
 
  其中,就餐計費細表(Eatdata2)的記錄就是把每餐總表(Eatdata1)的一條記錄按就餐員工平攤拆開,是個不折不扣的冗餘表。當然,也可以把每餐總表(Eatdata1)的部分欄位合併到就餐計費細表(Eatdata2)中,這樣每餐總表(Eatdata1)就成了冗餘表,不過這樣所設計出來的就餐計費細表重複資料更多,相比來說還是上面的方案好些。但是,就是就餐計費細表(Eatdata2)這個冗餘表,在做每月每人餐費統計的時候,大大簡化了程式設計的複雜度,只用類似這麼一條查詢語句即可統計出每人每月的寄餐次數和餐費總帳:
 
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
 
  想象一下,如果不用這個冗餘表,每次統計每人每月的餐費總帳時會多麻煩,程式效率也夠嗆。那麼,到底什麼時候可以增加一定的冗餘資料呢?我認為有2個原則:
 
  、使用者的整體需求。當使用者更多的關注於,對資料庫的規範記錄按一定的演算法進行處理後,再列出的資料。如果該演算法可以直接利用後臺資料庫系統的內嵌函式來完成,此時可以適當的增加冗餘欄位,甚至冗餘表來儲存這些經過演算法處理後的資料。要知道,對於大批量資料的查詢,修改或刪除,後臺資料庫系統的效率遠遠高於我們自己編寫的程式碼。
  、簡化開發的複雜度。現代軟體開發,實現同樣的功能,方法有很多。儘管不必要求程式設計師精通絕大部分的開發工具和平臺,但是還是需要了解哪種方法搭配哪種開發工具的程式更簡潔,效率更高一些。冗餘資料的本質就是用空間換時間,尤其是目前硬體的發展遠遠高於軟體,所以適當的冗餘是可以接受的。不過我還是在最後再強調一下:不要過多的依賴平臺和開發工具的特性來簡化開發,這個度要是沒把握好的話,後期維護升級會栽大跟頭的。 

 


相關文章