以文本方式查看主题 - Foxtable(狐表) (http://foxtable.com/bbs/index.asp) -- 专家坐堂 (http://foxtable.com/bbs/list.asp?boardid=2) ---- ★[转帖]浅谈数据库设计技巧 (http://foxtable.com/bbs/dispbbs.asp?boardid=2&id=2889) |
-- 作者:菜鸟foxtable -- 发布时间:2009/5/22 21:23:00 -- ★[转帖]浅谈数据库设计技巧 声明:此文章为转载! 数据库的最初雏形据说源自美国一个奶牛场的记账薄(纸质的,由此可见,数据库并不一定是存储在电脑里的数据^_^),里面记录的是该奶牛场的收支账目,程序员在将其整理、录入到电脑中时从中受到启发。当按照规定好的数据结构所采集到的数据量大到一定程度后,出于程序执行效率的考虑,程序员将其中的检索、更新维护等功能分离出来,做成单独调用的模块,这个模块后来就慢慢发展、演变成现在我们所接触到的数据库管理系统(DBMS)——程序开发中的一个重要分支。 下面进入正题,首先按我个人所接触过的程序给数据库设计人员的功底分一下类: 我个人正处于第三类的末期,所以下面所列出的一些设计技巧只适合第二类和部分第三类数据库设计人员。同时,由于我很少碰到有兴趣在这方面深钻下去的同行,所以文中难免出现错误和遗漏,在此先行声明,欢迎大家指正,不要藏私哦8) 一、树型关系的数据表 类别表_1(Type_table_1) 这样的设计短小精悍,完全满足3NF,而且可以满足用户的所有要求。是不是这样就行呢?答案是NO!Why? 我们来估计一下用户希望如何罗列出这个表的数据的。对用户而言,他当然期望按他所设定的层次关系一次罗列出所有的类别,例如这样: 看看为了实现这样的列表显示(树的先序遍历),要对上面的表进行多少次检索?注意,尽管类别1.1.1可能是在类别3.2之后添加的记录,答案仍然是N次。这样的效率对于少量的数据没什么影响,但是日后类型扩充到数十条甚至上百条记录后,单单列一次类型就要检索数十次该表,整个程序的运行效率就不敢恭维了。或许第二类程序员会说,那我再建一个临时数组或临时表,专门保存类型表的先序遍历结果,这样只在第一次运行时检索数十次,再次罗列所有的类型关系时就直接读那个临时数组或临时表就行了。其实,用不着再去分配一块新的内存来保存这些数据,只要对数据表进行一定的扩充,再对添加类型的数量进行一下约束就行了,要完成上面的列表只需一次检索就行了。下面是扩充后的数据表结构: 类别表_2(Type_table_2) 按照这样的表结构,我们来看看上面例子记录在表中的数据是怎样的: type_id type_name type_father type_layer 现在按type_layer的大小来检索一下:SELECT * FROM Type_table_2 ORDER BY type_layer 列出记录集如下: type_id type_name type_father type_layer 现在列出的记录顺序正好是先序遍历的结果。在控制显示类别的层次时,只要对type_layer字段中的数值进行判断,每2位一组,如大于0则向右移2个空格。当然,我这个例子中设定的限制条件是最多3层,每层最多可设99个子类别,只要按用户的需求情况修改一下type_layer的长度和位数,即可更改限制层数和子类别数。其实,上面的设计不单单只在类别表中用到,网上某些可按树型列表显示的论坛程序大多采用类似的设计。 或许有人认为,Type_table_2中的type_father字段是冗余数据,可以除去。如果这样,在插入、删除某个类别的时候,就得对type_layer 的内容进行比较繁琐的判定,所以我并没有消去type_father字段,这也正符合数据库设计中适当保留冗余数据的来降低程序复杂度的原则,后面我会举一个故意增加数据冗余的案例。 商品类型表(Wares_type) 供货厂商表(Wares_provider) 商品信息表(Wares_info) 你拿着这3个表给老板检查,老板希望能够再添加一个商品图片的字段,不过只有一部分商品有图片。OK,你在商品信息表(Wares_info)中增加了一个haspic的BOOL型字段,然后再建了一个新表——商品图片表(Wares_pic): 商品图片表(Wares_pic) 程序开发完成后,完全满足老板目前的要求,于是正式启用。一段时间后,老板打算在这套平台上推出新的商品销售,其中,某类商品全部都需添加“长度”的属性。第一轮折腾来了……当然,你按照添加商品图片表的老方法,在商品信息表(Wares_info)中增加了一个haslength的BOOL型字段,又建了一个新表——商品长度表(Wares_length): 商品长度表(Wares_length) 刚刚改完没多久,老板又打算上一批新的商品,这次某类商品全部需要添加“宽度”的属性。你咬了咬牙,又照方抓药,添加了商品宽度表(Wares_width)。又过了一段时间,老板新上的商品中有一些需要添加“高度”的属性,你是不是开始觉得你所设计的数据库按照这种方式增长下去,很快就能变成一个迷宫呢?那么,有没有什么办法遏制这种不可预见性,但却类似重复的数据库膨胀呢?我在阅读《敏捷软件开发:原则、模式与实践》中发现作者举过类似的例子:7.3 “Copy”程序。其中,我非常赞同敏捷软件开发这个观点:在最初几乎不进行预先设计,但是一旦需求发生变化,此时作为一名追求卓越的程序员,应该从头审查整个架构设计,在此次修改中设计出能够满足日后类似修改的系统架构。下面是我在需要添加“长度”的属性时所提供的修改方案: 去掉商品信息表(Wares_info)中的haspic字段,添加商品额外属性表(Wares_ex_property)和商品额外信息表(Wares_ex_info)2个表来完成添加新属性的功能。 商品额外属性表(Wares_ex_property) 商品额外信息表(Wares_ex_info) 在商品额外属性表(Wares_ex_property)中添加2条记录: 再在整个电子商务平台的后台管理功能中追加一项商品额外属性管理的功能,以后添加新的商品时出现新的属性,只需利用该功能往商品额外属性表(Wares_ex_property)中添加一条记录即可。不要害怕变化,被第一颗子弹击中并不是坏事,坏的是被相同轨道飞来的第二颗、第三颗子弹击中。第一颗子弹来得越早,所受的伤越重,之后的抵抗力也越强8)(待续) 资料引用:http://www.knowsky.com/4937.html [此贴子已经被作者于2009-5-22 21:31:37编辑过]
|
-- 作者:菜鸟foxtable -- 发布时间:2009/5/22 21:23:00 -- 三、多用户及其权限管理的设计 下面看看如何自行设计一套比较灵活的多用户管理模块,即该数据库管理软件的系统管理员可以自行添加新用户,修改已有用户的权限,删除已有用户。首先,分析用户需求,列出该数据库管理软件所有需要实现的功能;然后,根据一定的联系对这些功能进行分类,即把某类用户需使用的功能归为一类;最后开始建表: 用户组表(User_group) 用户表(User_table) 采用这种用户组的架构设计,当需要添加新用户时,只需指定新用户所属的用户组;当以后系统需要添加新功能或对旧有功能权限进行修改时,只用操作功能表和用户组表的记录,原有用户的功能即可相应随之变化。当然,这种架构设计把数据库管理软件的功能判定移到了前台,使得前台开发相对复杂一些。但是,当用户数较大(10人以上),或日后软件升级的概率较大时,这个代价是值得的。
书籍表(Book_table) 借阅用户表(Renter_table) 借阅记录表(Rent_log) 为了实现按批查询借阅记录,我们可以再建一个表来保存批量借阅的信息,例如: 批量借阅表(Batch_rent) 这样的设计好吗?我们来看看为了列出某个用户某次借阅的所有书籍,需要如何查询?首先检索批量借阅表(Batch_rent),把符合条件的的所有记录的rent_id字段的数据保存起来,再用这些数据作为查询条件带入到借阅记录表(Rent_log)中去查询。那么,有没有什么办法改进呢?下面给出一种简洁的批量设计方案,不需添加新表,只需修改一下借阅记录表(Rent_log)即可。修改后的记录表(Rent_log)如下: 借阅记录表(Rent_log) 其中,同一次借阅的batch_no和该批第一条入库的rent_id相同。举例:假设当前最大rent_id是64,接着某用户一次借阅了3本书,则批量插入的3条借阅记录的batch_no都是65。之后另外一个用户租了一套碟,再插入出租记录的rent_id是68。采用这种设计,查询批量借阅的信息时,只需使用一条标准T_SQL的嵌套查询即可。当然,这种设计不符合3NF,但是和上面标准的3NF设计比起来,哪一种更好呢?答案就不用我说了吧。
员工表(Clerk_table) 每餐总表(Eatdata1) 就餐计费细表(Eatdata2) 其中,就餐计费细表(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个原则: 1、用户的整体需求。当用户更多的关注于,对数据库的规范记录按一定的算法进行处理后,再列出的数据。如果该算法可以直接利用后台数据库系统的内嵌函数来完成,此时可以适当的增加冗余字段,甚至冗余表来保存这些经过算法处理后的数据。要知道,对于大批量数据的查询,修改或删除,后台数据库系统的效率远远高于我们自己编写的代码。 资料引用:http://www.knowsky.com/4938.html |
-- 作者:yangming -- 发布时间:2009/5/22 22:09:00 -- 受益非浅啊!谢谢楼主 |
-- 作者:wcs -- 发布时间:2009/5/22 22:40:00 -- 好贴没有顶啊? |
-- 作者:wcs -- 发布时间:2009/5/22 23:17:00 -- 我没系统学习过数据库, 但是:“一、树型关系的数据表”中说的,我几年前在易表中都有用到,因为这样子方便很多,写代码很方便,也只是增加了一行而已,这类似于会计科目的编码,讲究级别的! ------看来我是无师自通啊! 继续往后看...... |
-- 作者:wcs -- 发布时间:2009/5/22 23:49:00 -- “二、商品信息表的设计”,用户经常要增加属性,这种情况我遇到不多,通常只有一两次 没想过用这个方法,楼主介绍的这个思路是好,建立一个两边关联的表,一步到位了。 |
-- 作者:wcs -- 发布时间:2009/5/22 23:56:00 -- “三、多用户及其权限管理的设计” 用事件编程,对每一个事件,可以判断一下用户组,决定有无权限。这样搞的话权限设置代码是太分散了,不好管理但很灵活。 把它们集中在一起,在更换用户事件中一次设置确定,是可以集中管理很好。易表里面的权限设置就是这样搞的。 但是我很懒,没有想过要用自己设计的权限设置表。 因为,我想这样搞有一个问题:自己的每一个功能,是不是都要确定一个名称,好在权限表里面引用确定下来,这个工作量比较大。 |
-- 作者:wcs -- 发布时间:2009/5/22 23:59:00 -- “四、简洁的批量m:n设计” 这个不符合3NF,但是这样很实在。 要在代码的复杂 和 数据表的复杂 之间取舍, 我想代码简单一些是好的,懒人有懒法! 哈哈! |
-- 作者:wcs -- 发布时间:2009/5/23 0:03:00 -- “五、冗余数据的取舍” 这和 四 差不多,用了一个中间表“就餐计费细表(Eatdata2)” ,先行计算一下,并且两边关联。 查询好方便!! 这也是代码的复杂 和 数据表的冗余 之间的取舍。 我写代码的水平实在是不太行,所以我有时用中间表计算一下子。 楼主的贴子,让我进一步学习了一下数据库的表设计思路! 谢谢! 建议老大在帮助文件中 ,也说一下类似的东西,指导一下大家! |
-- 作者:狐狸爸爸 -- 发布时间:2009/5/23 7:22:00 -- 呵呵,不错,我顶 |