小天:按照你的说法,我理解为,设计数据库其实就是设计数据库中的表。如果真是这么重要的话,到底要注意些什么才能够设计好一个数据库呢?
:一个宗旨“尽量少的表,每个表中尽量少的列,合理的表结构”。
小天:说的好抽象,具体点,比如设计表的时候可以有什么辅助工具;什么时候用什么数据类型;谁跟谁之间有了关系,什么样的关系;还有你可以根据你这么多年的经验给点实际性的建议嘛。
:厉害,居然问出什么有深度的问题了,下面我们就来一点一点的整理下。
第一:首先要考虑的是咱们这个数据库的主要作用是什么?至少包含哪些数据?这些数据又分别属于那些实体对象?对象之间又存在什么样的关系?比如说新闻文章管理系统的数据库,它要存放的数据至少包括:文章分类,文章标题,发文时间,作者;而既然是管理系统,那么肯定会有人要添加删除修改文章,那么就延伸出需要管理员,有管理员了就存在账号,密码;如果还要有对文章的评论功能,那就还有评论的标题,评论的类容,评论人姓名,评论时间等。这么多需要存放的数据,如何归类,归类后又如何整理相互之间的关系呢?这就需要用到工具。
第二: ,建立E-R模型的工具很多,甚至纸笔也算是一种工具。而E-R图的画法其实也很多。它的主要作用是将所有要存入数据库的数据归类、整理成一个个的分类,这个分类被称之为实体,而被归如这个分类的数据则被称之为实体的属性。不同的实体之间存在关联,比如文章和管理员之间就存在谁发布了文章,文章和评论之间存在某条评论是属于那篇文章,这样的关系,在E-R模型上就必须把这些体现出来。具体E-R的工具和画法稍后有一小节来讲,这里先不赘述。
第三:每一个实体就是一个表,而实体的属性就是这个表的列,那么现在问题就出来了,到底什么样的列该用什么样的数据类型,比如文章的标题该用什么数据类型呢?
小天:NCHAR(50)行不?因为我们这边是采用Unicode字符,而文章标题字符串,50个字足够了。
老田:至于你说的什么类型先姑且不论,来打个赌,看如下代码,不准运行,根据上一章学习的内容自己来想,如果错了,到本书后面提供的纠错网站上去报个到,错了的人要给对了的人做一件事或者回答一个类似国王游戏的问题。
declare @txta nchar(10)='你猜最终我这一行能够显示多少个字'--16个字,显示?个字 declare @txtb char(10)='知道为什么我比他显示得多' --12个字,显示?个字 select @txta,@txtb |
天轰穿的博客 我的QQ技术交流群:84518638
接着来看你用的NCHAR(50)有不有问题,首先我们说什么情况下用NCHAR类型呢?一是在Unicode字符的时候用N开头的类型,其次只有在数据长度基本差不多的情况采用。但是文章的标题会每一个都固定长度吗?而且char类型最致命的一个缺陷是,只要用它的数据,只要长度不够,就自动填充空格,问题出来,如果连续10万行数据的这一列都只有40个字符,那么每行都增加10个空格,这样下来10万*10字符的空间实际上就白白的被占用了。虽然有人说现在磁盘成本已经越来越廉价,但是我觉得吧,这不是钱的问题,因为数据库体积越大,处理数据的效率必然受影响。再比如说,用户注册中年龄的问题,肯定首选tinyint,因为目前人类的年龄不可能超过255岁,而这个类型只占1字节的空间,如果习惯性的用int类型,咋一看也没有问题,可惜就白白浪费了3字节的空间,因为int是4字节的长度。
小天:我明白你意思了,这第三点总结下就是要做到哪怕一字节的空间也不能浪费,但是也不能节约得整个数据库都不好用了。
天轰穿的博客
我的QQ技术交流群:84518638第四:允许为空和默认值。这是什么意思呢,首先要明确什么是空值,什么是默认值,这里的空值既不是0也不是空字符,而表示未知,用null表示。这就有问题了,首先如果处在变长类型列中的null值本身虽然不占空间,但是它所在列确实实实在在的要占用空间的。再则null比较特殊,数据库要对null字段进行额外的操作,所以如果表中有较多的null字段时会影响数据库的性能,还有一点是我们现在想不到,但将来一定遇到的,null字段会给我们编程带来一些不大不小的麻烦,比如制造一些bug。
所以,一句话,尽量少用允许列为空,如果一定要允许也尽量将之靠后。
小天:我要是做个会员管理系统,除了会员账号、密码、电子邮箱之外的资料我觉得都可以为空,但是总不可能数据库就只有这三个字段撒。
老田:当然不可能这么少啦,但是为什么不可以做成两张表,一张为基本信息表 一张详细信息表。将用户可能一般都不填写的资料单独放在详细资料表,那么如果用户不填写详细资料,这个表里就少一行数据。
小天:这个主意不错哦,我还有个问题,就是用户注册时间的问题,我肯定希望记录下用户是什么时候注册的,但是这行人家用户不可能愿意写,就算写也可能乱写,咋办?
老田:这个简单,用默认值。比如要解决注册日期的问题就在用户基本信息表中加一列注册日期(不要用中文),给这一列设置默认值“GETDATE()”,然后每次添加信息的时候就不去管这一列了,反正它会在执行插入数据的时候把系统当前最新时间加入当前行。
天轰穿的博客
我的QQ技术交流群:84518638第五:主键的问题,来看个实际的例子。公安部要通缉孙悟空,这问题就出来了,全国叫孙悟空的人可能不只一猴,所以单纯靠名字是不能通缉的。长相?不行,全国的猴子多了去了,长得像的也挺多。我们这里不是YY小说,不玩灵魂气息。哪咋办呢,就是给全国的猴子办身份证,每个身份证上的编号绝对不能相同。于是以后再要通缉孙悟空的话,就好办了。我们的数据表中数据也一样,每一行都需要一个绝不重复的标志作为主键。
小天:你这意思就是每张表都需要一个主键?我的会员管理系统中难道也让人家注册的时候填写身份证号?
老田:让人注册的时候写身份证号一般来说是不现实的,咋办呢?就只好我们额外的加上一行作为主键了,这也是在数据库中常见的做法,为表增加一行看起来风马牛不相及的行,作用只有一个,就是做标志主键。
第六:约束和规则,用于确保数据完整有效性,一旦定义了约束和规则,那么只有满足这些条件的数据才可一倍插入数据库。比如要求注册会员的性别要么是男,要么是女,绝对不允许第三种情况,再比如年龄只能是18-80岁,其他注册不了。
第七:外键关系,比如会员管理系统,如果所有会员都是同样当然无所谓,但如果分为普通会员,金、银、铜牌会员几种类型,这时候就需要好好的思量下了。到底是在会员信息表中增加一个列来存储会员类型的名称呢还是单独用另外一张表来存储会员名称,再把两张表关联起来。如下处理方式
第一种方式
主键 | 登录名 | 密码 | 会员类型 | 注册日期 |
1 | Thc | 123456 | 金牌会员 | 2009-10-21 |
2 | Abc | 123456 | 银牌会员 | 2009-10-22 |
3 | Cda | 123456 | 普通会员 | 2009-10-20 |
第二种方式,两张表
第二种方式中会员信息表中的会员类型列就是外键。这样做很明显就违背了本章最开始说的表要尽量少这个宗旨。可有时候是有必要的,还是以会员中心这个案例来说,我还是选择用第二种方式。假设会员类型分类可能随着时间的过去,企业的发展,会员会有很多,那么这个会员类型列因为只存储一个int类型的字段,所以空间就节约出很多来。
另外一些原因就是考虑到方便编程和程序的扩展,比如增加新的会员类型分类等等。
第八:考虑是否使用索引,索引也是一种数据库对象,是加快对数据表中数据检索的一种手段,是提高数据库使用效率的一种重要方法。于是要在那些列上使用索引,对那些列不使用索引,是使用聚簇索引还是使用费聚簇索引,是否使用全文索引,等等,很多问题需要认真的去思考。