快捷搜索:

浅谈数据库设计技巧(上)

说到数据库,我觉得不能不先谈数据布局。1996年,在我初入大年夜学进修谋略机编程时,当时的师长教师就奉告我们说:谋略机法度榜样=数据布局+算法。只管现在的法度榜样开拓已由面向历程为主慢慢过渡到面向工具为主,但我照样深深附和8年前师长教师的奉告我们的公式:谋略机法度榜样=数据布局+算法。面向工具的法度榜样开拓,要做的第一件事便是,先阐发全部法度榜样中需处置惩罚的数据,从中提掏出抽象模板,以这个抽象模板设计类,再在此中慢慢添加处置惩罚其数据的函数(即算法),着末,再给类中的数据成员和函数划分造访权限,从而实现封装。

数据库的最初雏形听说源自美国一个奶牛场的记账薄(纸质的,由此可见,数据库并不必然是存储在电脑里的数据^_^),里面记录的是该奶牛场的出入账目,法度榜样员在将其收拾、录入到电脑中时从中受到启迪。当按照规定好的数据布局所采集到的数据量大年夜到必然程度后,出于法度榜样履行效率的斟酌,法度榜样员将此中的检索、更新掩护等功能分离出来,做成零丁调用的模块,这个模块后来就逐步成长、蜕变成现在我们所打仗到的数据库治理系统(DBMS)——法度榜样开拓中的一个紧张分支。

下面进入正题,首先按我小我所打仗过的法度榜样给数据库设计职员的功底分一下类:

1、没有系统进修过数据布局的法度榜样员。这类法度榜样员的作品每每只是他们的即兴玩具,他们每每习气只设计有限的几个表,实现某类功能的数据整个塞在一个表中,各表之间险些毫无关联。网上不少的免费治理软件都是这样的器械,当法度榜样功能有限,数据量不多的时刻,其法度榜样运行起来没有什么问题,然则假如用其治理对照紧张的数据,风险性异常大年夜。

2、系统进修过数据布局,然则还没有开拓过对法度榜样效率要求对照高的治理软件的法度榜样员。这类人多数刚从黉舍卒业不久,他们在设计数据库表布局时,严格按照教科书上的规定,逝世扣E-R图和3NF(别泄气,所有的数据库设计高手都是从这一步开始的)。他们的作品,对付一样平常的access型轻量级的治理软件,已经够用。然则一旦该系统必要添加新功能,原有的数据库表差不多得进行大年夜换血。

3、第二类法度榜样员,在经历过数次法度榜样效率的提升,以及功能进级的折腾后,终于进级成为数据库设计的老鸟,第一类法度榜样员眼中的高人。这类法度榜样员可以胜任二十个表以上的中型商业数据治理系统的开拓事情。他们知道该在什么样的环境下保留必然的冗余数据来前进法度榜样效率,而且其设计的数据库可拓展性较好,当用户必要添加新功能时,原稀有据库表只需做少量改动即可。

4、在经历过上十个类似数据库治理软件的重复设计后,第三类法度榜样员中坚持下来没有转行,而是盼望从中找出“偷懒”秘诀的有心人会逐步醒悟,从而完成量变到质变的转换。他们所设计的数据库表布局有必然的远见,能够猜测到未来功能进级所必要的数据,从而预先留下伏笔。这类法度榜样员今朝大年夜多晋级成数据掘客方面的高档软件开拓职员。

5、第三类法度榜样员或第四类法度榜样员,在对现有的各家数据库治理系统的道理和开拓都有必然的研究后,要么在其根基长进行二次开拓,要么自行开拓一套有自立版权的通用数据库治理系统。

我小我正处于第三类的末期,以是下面所列出的一些设计技术只得当第二类和部分第三类数据库设计职员。同时,因为我很少碰着有兴趣在这方面深钻下去的同业,以是文中难免呈现差错和漏掉,在此先行声明,迎接大年夜家斧正,不要藏私哦8)

一、树型关系的数据表

不少法度榜样员在进行数据库设计的时刻都碰到过树型关系的数据,例如常见的种别表,即一个大年夜类,下面有多少个子类,某些子类又有子类这样的环境。当种别不确定,用户盼望可以在随意率性种别下添加新的子类,或者删除某个种别和其下的所有子类,而且估计今后其数量会慢慢增长,此时我们就会斟酌用一个数据表来保存这些数据。按照教科书上的教育,第二类法度榜样员大年夜概会设计出类似这样的数据表布局:

种别表_1(Type_table_1)

名称     类型    约束前提   阐明

type_id int 无重复  种别标识,主键

type_name   char(50)不容许为空类型名称,不容许重复

type_fatherint不容许为空该类其余父种别标识,假如是顶节点的话设定为某个独一值

这样的设计短小精悍,完全满意3NF,而且可以满意用户的所有要求。是不是这样就行呢?谜底是NO!Why?

我们来预计一下用户盼望若何列举出这个表的数据的。对用户而言,他当然期望按他所设定的层次关系一次列举出所有的种别,例如这样:

总种别

种别1

种别1.1

种别1.1.1

种别1.2

种别2

种别2.1

种别3

种别3.1

种别3.2

……

看看为了实现这样的列表显示(树的先序遍历),要对上面的表进行若干次检索?留意,只管种别1.1.1可能是在种别3.2之后添加的记录,谜底仍旧是N次。这样的效率对付少量的数据没什么影响,然则日后类型扩充到数十条以致上百笔记录后,单单列一次类型就要检索数十次该表,全部法度榜样的运行效率就不敢奉承了。或许第二类法度榜样员会说,那我再建一个临时数组或临时表,专门保存类型表的先序遍历结果,这样只在第一次运行时检索数十次,再次列举所有的类型关系时就直接读那个临时数组或临时表就行了。着实,用不着再去分配一块新的内存来保存这些数据,只要对数据表进行必然的扩充,再对添加类型的数量进行一下约束就行了,要完成上面的列表只需一次检索就行了。下面是扩充后的数据表布局:

种别表_2(Type_table_2)

名称     类型    约束前提   阐明

type_id int 无重复  种别标识,主键

type_name   char(50)不容许为空类型名称,不容许重复

type_fatherint不容许为空该类其余父种别标识,假如是顶节点的话设定为某个独一值

type_layerchar(6)限制3层,初始值为000000类其余先序遍历,主要为削减检索数据库的次数

按照这样的表布局,我们来看看上面例子记录在表中的数据是如何的:

type_idtype_nametype_fathertype_layer

1总种别0000000

2种别11010000

3种别1.12010100

4种别1.22010200

5种别21020000

6种别2.15020100

7种别31030000

8种别3.17030100

9种别3.27030200

10种别1.1.13010101

……

现在按type_layer的大年夜小来检索一下:SELECT * FROM Type_table_2 ORDER BY type_layer

列出记录集如下:

type_idtype_nametype_fathertype_layer

1总种别0000000

2种别11010000

3种别1.12010100

10种别1.1.13010101

4种别1.22010200

5种别21020000

6种别2.15020100

7种别31030000

8种别3.17030100

9种别3.27030200

……

现在列出的记录顺序恰正是先序遍历的结果。在节制显示类其余层次时,只要对type_layer字段中的数值进行判断,每2位一组,如大年夜于0则向右移2个空格。当然,我这个例子中设定的限定前提是最多3层,每层最多可设99个子种别,只要按用户的需求环境改动一下type_layer的长度和位数,即可变动限定层数和子种别数。着实,上面的设计不单单只在种别表顶用到,网上某些可按树型列表显示的论坛法度榜样大年夜多采纳类似的设计。

或许有人觉得,Type_table_2中的type_father字段是冗余数据,可以撤除。假如这样,在插入、删除某个类其余时刻,就得对type_layer 的内容进行对照繁琐的鉴定,以是我并没有消去type_father字段,这也正相符数据库设计中适当保留冗余数据的来低落法度榜样繁杂度的原则,后面我会举一个有意增添数据冗余的案例。

二、商品信息表的设计

假设你是一家百货公司电脑部的开拓职员,某天老板要求你为公司开拓一套网上电子商务平台,该百货公司稀有千种商品出售,不过今朝仅盘算先在网上贩卖数十种方便运输的商品,当然,今后可能会陆续在该电子商务平台上增添新的商品出售。现在开始进行该平台数据库的商品信息表的设计。每种出售的商品都邑有相同的属性,如商品编号,商品名称,商品所属种别,相关信息,供货厂商,内含件数,库存,进货价,贩卖价,优惠价。你很快就设计出4个表:商品类型表(Wares_type),供货厂商表(Wares_provider),商品信息表(Wares_info):

商品类型表(Wares_type)

名称     类型    约束前提   阐明

type_id int 无重复  种别标识,主键

type_name   char(50)不容许为空类型名称,不容许重复

type_fatherint不容许为空该类其余父种别标识,假如是顶节点的话设定为某个独一值

type_layerchar(6)限制3层,初始值为000000类其余先序遍历,主要为削减检索数据库的次数

供货厂商表(Wares_provider)

名称     类型    约束前提   阐明

provider_idint 无重复  供货牌号识,主键

provider_name char(100)不容许为空供货商名称

商品信息表(Wares_info)

名称    类型    约束前提   阐明

wares_idint 无重复  商品标识,主键

wares_namechar(100)不容许为空商品名称

wares_type   int不容许为空           商品类型标识,和Wares_type.type_id关联

wares_infochar(200)容许为空相关信息

providerint不容许为空供货厂牌号识,和Wares_provider.provider_id关联

setnumint初始值为1内含件数,默觉得1

stockint初始值为0库存,默觉得0

buy_pricemoney不容许为空进货价

sell_pricemoney不容许为空贩卖价

discountmoney不容许为空优惠价

你拿着这3个表给老板反省,老板盼望能够再添加一个商品图片的字段,不过只有一部分商品有图片。OK,你在商品信息表(Wares_info)中增添了一个haspic的BOOL型字段,然后再建了一个新表——商品图片表(Wares_pic):

商品图片表(Wares_pic)

名称    类型    约束前提   阐明

pic_idint 无重复  商品图片标识,主键

wares_idint不容许为空所属商品标识,和Wares_info.wares_id关联

pic_address  char(200)不容许为空           图片寄放路径

法度榜样开拓完成后,完全满意老板今朝的要求,于是正式启用。一段光阴后,老板盘算在这套平台上推出新的商品贩卖,此中,某类商品整个都需添加“长度”的属性。第一轮折腾来了……当然,你按照添加商品图片表的老措施,在商品信息表(Wares_info)中增添了一个haslength的BOOL型字段,又建了一个新表——商品长度表(Wares_length):

商品长度表(Wares_length)

名称    类型    约束前提   阐明

length_idint 无重复  商品图片标识,主键

wares_idint不容许为空所属商品标识,和Wares_info.wares_id关联

length char(20)不容许为空           商品长度阐明

刚刚改完没多久,老板又盘算上一批新的商品,此次某类商品整个必要添加“宽度”的属性。你咬了咬牙,又照方抓药,添加了商品宽度表(Wares_width)。又过了一段光阴,老板新上的商品中有一些必要添加“高度”的属性,你是不是开始感觉你所设计的数据库按照这种要领增长下去,很快就能变成一个迷宫呢?那么,有没有什么法子遏制这种弗成预见性,但却类似重复的数据库膨胀呢?我在涉猎《敏捷软件开拓:原则、模式与实践》中发明作者举过类似的例子:7.3 “Copy”法度榜样。此中,我异常附和敏捷软件开拓这个不雅点:在最初险些不进行预先设计,然则一旦需求发生变更,此时作为一名追求卓越的法度榜样员,应该从头检察全部架构设计,在这次改动中设计出能够满意日后类似改动的系统架构。下面是我在必要添加“长度”的属性时所供给的改动规划:

去掉落商品信息表(Wares_info)中的haspic字段,添加商品额外属性表(Wares_ex_property)和商品额外信息表(Wares_ex_info)2个表来完成添加新属性的功能。

商品额外属性表(Wares_ex_property)

名称    类型    约束前提   阐明

ex_pidint 无重复  商品额外属性标识,主键

p_namechar(20)不容许为空额外属性名称

商品额外信息表(Wares_ex_info)

名称    类型    约束前提   阐明

ex_iidint 无重复  商品额外信息标识,主键

wares_idint不容许为空所属商品标识,和Wares_info.wares_id关联

property_id int不容许为空           商品额外属性标识,和Wares_ex_property.ex_pid关联

property_valuechar(200)不容许为空商品额外属性值

在商品额外属性表(Wares_ex_property)中添加2笔记录:

ex_pidp_name

1商品图片

2商品长度

再在全部电子商务平台的后台治理功能中追加一项商品额外属性治理的功能,今后添加新的商品时呈现新的属性,只需使用该功能往商品额外属性表(Wares_ex_property)中添加一笔记录即可。不症结怕变更,被第一颗枪弹击中并不是坏事,坏的是被相同轨道飞来的第二颗、第三颗枪弹击中。第一颗枪弹来得越早,所受的伤越重,之后的抵抗力也越强8)(待续)

您可能还会对下面的文章感兴趣: