我们该如何设计数据库

上一篇 / 下一篇  2012-05-03 10:35:36 / 个人分类:数据库

数据库该如何设计,一直以来都是一个仁者见仁智者见智的问题。

bm+?5X8`'SD [0]0  对于某一种数据库设计,并不能简单的用好与不好来区分。或许真的应了那句话,没有最好,只有最适合。讨论某种数据库设计的时候,应该在某种特定的需求环境下讨论。51Testing软件测试网_s+ve8}N^8T

/AJ,f L;U}0  下面来讨论一下在项目中经常碰到的用户的联系方式储存的问题。

$B4F;q9t/[c+u:c0

u2I ahT,vPt0  我在这里套用之前网络上流行“普通——文艺——二逼”的分类方式来描述我下文中提及的三种数据库设计思路,并且通过查询数据(对数据增删改,三种设计要付出的代码成本都差不多)和数据库面临需求变动两个方面来思考这三种设计各有怎样的优劣。

)c C%Z] b!A/X0

%lu)Y\:I RlFQd0  普通青年:51Testing软件测试网[ t!oh^.d#q

51Testing软件测试网&|3k4a.b,n,wx!n7d

  或许我们都这样设计过数据库51Testing软件测试网_y[i$p Z

,b$C|/G y ^ M%ho0  学生表 tb_Student:51Testing软件测试网$EkH;L-L|[*~H

Namevarchar(100)名字
Telphonevarchar(200)联系电话
Emailvarchar(200)你懂的
Faxvarchar(200)传真

  这应该是最容易想到的一种思路,简单、明了。

Q0X&Zt$j#ad(qT0

  比如说我要查询某个人的联系方式,那么我只用一条语句就能实现:

8D9uNQeY(l.\0
select Name,Telphone,Email,Fax from 表 where 条件

  在查询的时候,这种数据库设计十分清晰,没有任何思维的难度,没有任何逻辑的挑战。但是当面临需求变动的时候,那将会是一场灾难。

$Grb2e9VK0

  比如现在要新增一类用户:校长。那么我们要如何处理?51Testing软件测试网W#] _?9I4T&Le,pc+Z

  答案是:再加一张表 tb_Headmaster。51Testing软件测试网9sw F2V_'k

  事实上,再加一张表其实修改并不大,因为我们完全不需要修改学生表的存储逻辑,换句话说,这种设计是遵循了开闭原则的。51Testing软件测试网zqnCG$R

  但如果学生要添加一种联系方式HomePhone的时候,灾难发生了,怎么办?

.^3NF@vk0

  在tb_Student中加一列HomePhone?这意味着至少要修改整个Model层(或者说DAL层),这种改动是十分巨大的,而且容易造成错误。51Testing软件测试网P0N1M6s/F6a1axX

  或者再建一张表tb_Student2,来储存HomePhone,然后以ID来关联两张表?按改动规模来说,这种改动相对简单而且不容易出错,但是在今后的维护中会增加逻辑成本。当你一而再再而三的以这样的方式来应对需求变动的时候,你的程序将变得不可理解。51Testing软件测试网*n$e B?F*H/Yq

  文艺青年:

.\Q\x duv(`0
UserRoleint对应用户类型(None = 0, Student = 1, Teacher = 2, Headmaster = 4)
OwnerIDint对应用户ID
ContactMethodint联系方式(None = 0, Email = 1, HomePhone = 8, WorkPhone = 16,MobilePhone = 32,Fax=64)
ContactInfovarchar(255)联系信息

  这种是一个多对多关系。当我们要查询某个用户对应的联系方式的时候,那是一场逻辑上的浩劫:51Testing软件测试网~3s8aBw"aC7FA oWk{

select ContactInfo from 表 where UserRole=某种用户类型 and OwnerID=某用户ID

  这种写法是一次性取出某个用户所有的联系方式,包括Email,HomePhone,WorkPhone等,之后我们可以在程序中判断ContactMethod的类型,将具体的联系方式加以区分。你可以简单的想到用switch-case的写法,类似这样:51Testing软件测试网B;A x BM)MY

  1. var contact = 上面的SQL语句取出来的用户所有的联系方式;  
  2.             foreach (var item in contact)  
  3.             {  
  4.                 switch (item.ContactMethod)  
  5.                 {  
  6.                     case ContactMethod.WorkPhone:  
  7.                         txtWorkPhone.Text = item.ContactInfo;break;  
  8.                     case ContactMethod.Email:  
  9.                         txtEmail.Text = item.ContactInfo;  
  10.                         break;  
  11.                     case ContactMethod.Fax:  
  12.                         txtFax.Text = item.ContactInfo;  
  13.                         break;  
  14.                     case ContactMethod.OtherPhone:  
  15.                         txtOtherPhone.Text = item.ContactInfo;  
  16.                         break;  
  17.                     case ContactMethod.MobilePhone:  
  18.                         txtMobilePhone.Text = item.ContactInfo;  
  19.                         break;  
  20.                 }  
  21.             }
当然你也可以尝试下面这种写法,我个人认为这种写法更优雅

R4U\Mp051Testing软件测试网r@)Koe1s.p/E-dR

  1. var contact = 上面的SQL语句取出来的用户所有的联系方式;              
  2. txtWorkPhone.Text = (from a in contact  
  3.                      where a.ContactMethod == ContactMethod.Work_Phone  
  4.                      select a.ContactInfo).ToString();  
  5. //后面以此类推,你懂的
51Testing软件测试网v5U.Ad2y8K

  注意,请不要试图使用类似下面这类语句来查询某用户的联系方式:51Testing软件测试网-U+c1R6[Y;f)PT u

AuC.e{\051Testing软件测试网r!JX%_!`-A2A%W

  1. select ContactInfo from 表 where UserRole=某种用户类型 and OwnerID=某用户ID and ContactMethod=1    //取出某用户的Email  
  2. select ContactInfo from 表 where UserRole=某种用户类型 and OwnerID=某用户ID and ContactMethod=8    //取出某用户的HomePhone
51Testing软件测试网4lI4u a ?&HU

  相信我,这种做法非常愚蠢:每当你要取出这个用户的一种联系方式,就要和数据库建立一次连接,打开/关闭一次数据库;这种做法代价是十分巨大的,即使有数据库连接池,即使有数据库缓存,都应该避免这种愚蠢的做法.51Testing软件测试网0Cs:j](n'Du

n-~)g6H1I`!z0  唔,用了那么多的代码,终于查出了某个用户的联系信息了。反正我个人觉得这种设计方式在查询的时候,是逻辑上的浩劫。什么?你说你很享受?好吧,看来是我脑容量不够……

Zr@G4OkdeI051Testing软件测试网-N?;@v {,I!XDi

  不过当我们面临需求变动的时候,那就非常愉快了。51Testing软件测试网 S0c!OH3_,R,W^

J'R Tv I8O'~v3^%U0  什么,要加一类用户?简单,UserRole加一个枚举就好了。

;Aw2w@+H Hy+G#u cD0

O5C2\1Y`)l J h0  什么,要加一种联系方式?ContactMethod加一个枚举就OK。51Testing软件测试网 ie'd*\%[(cd`

51Testing软件测试网 x#t's ~ l5e

  使用了这种表设计的时候,相信你会微笑着面对需求变动的

E4P:HD#Q0

#hw"_1JP4E0  二逼青年51Testing软件测试网6H6K| ~KEj xJ O

51Testing软件测试网 mE:u'mw-Mv ^

  昨天和同事也探讨了下这个问题,按他的说法就是:哪个表要联系方式,我就扔个字段进去,存json

/N#P+dB9I"c6Z+Q ];c0

i0{*Ebc(Tc)~3R3g0
Contactvarchar(8000)用于储存json

  举例来说,有这么一个用户:51Testing软件测试网4u#k3^]a!V

51Testing软件测试网 y3H.m5s(|#W+g0~.s

ID:1Name:张三Telphone:1234Email:123@123.comFax:5678

  那么数据库中就这样存:51Testing软件测试网)K%d%|}Adqu

  [{"ID":1,"Name":"张三","Telphone":"1234","Email":"123@123.com","Fax":"5678"}]

!Lp,b2cv*Zp0

  当我听到这种设计思路的时候,虎躯微微一震:靠,这都行。按这种设计,我整张表都放进一个json里面一股脑的存进去就算了。不过震惊之后仔细想一想,其实这种设计也是有可取之处

|n&m9C;g#F-](~0

  首先,从查询来说,和普通青年一样,只需一句SQL:51Testing软件测试网%B*Hj O5N}.~

51Testing软件测试网6F'Fl.k FtX B"J

select Contact from 表 where 条件

  查询之后,就可以通过json处理函数将想要的数据取出来,在此就不赘述了51Testing软件测试网 s[W&_:[)}7qp

  那么当面临需求变动的时候会发生什么:51Testing软件测试网FFzi#I5lX}

  加一类用户的时候,要添加一张表。也是符合开闭原则,原有代码没有改动。51Testing软件测试网p@0}-V#nUZ;KH

  加一种联系方式,只用存json的时候多存一点东西。51Testing软件测试网5nV$h$~i5?

  不过这种设计如果要更新某条数据的话要稍微麻烦一点:先查询一条数据,重组json之后再Update。51Testing软件测试网wD3q:em8o+_3y v

  写了那么多,希望已经将想要表达的问题表达清楚了。不足之处望海涵,也欢迎留言斧正。51Testing软件测试网(s,X-P;j9P9tO

  PS:真的是一个规律,一个月才能写出一篇博客……51Testing软件测试网D4Le#F$H4qRHx

  再PS:就快能回家了,高兴,开心。51Testing软件测试网dXQ;?e7wF,Q


TAG:

 

评分:0

我来说两句

Open Toolbar