- 浏览: 200220 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
dawnyu:
太厉害了,专门登陆支持一下楼主
eclipse配置tomcat,访问http://localhost:8080出现404错误 -
wanghuan_em:
我怎么还是没成功...
在MyEclipse中安装Freemarker插件 -
xiayuhuashang:
在MyEclipse中安装Freemarker插件 -
PhoenixHorse:
就是啊,为什么要替换包呢,难道你存在其它兼容问题?
在MyEclipse中安装Freemarker插件 -
yueyuxiang:
楼主威武!我成功了
eclipse配置tomcat,访问http://localhost:8080出现404错误
便于自己阅读,有部分删改。转自:http://yangguo.iteye.com/blog/445905
计算机程序=数据结构+算法。 面向对象的程序开发,要做的第一件事就是,先分析整个程序中需处理的数据,从中提取出抽象模板,以这个抽象模板设计类,再在其中逐步添加处理其数据的函数(即算法),最后,再给类中的数据成员和函数划分访问权限,从而实现封装。
下面进入正题,首先按我个人所接触过的程序给数据库设计人员的功底分一下类:
1、没有系统学习过数据结构的程序员,他们往往习惯只设计有限的几个表,实现某类功能的数据全部塞在一个表中,各表之间几乎毫无关联,当程序功能有限,数据量不多的时候,其程序运行起来没有什么问题,但是如果用其管理比较重要的数据,风险性非常大。
2、系统学习过数据结构,但是还没有开发过对程序效率要求比较高的管理软件的程序员。他们在设计数据库表结构时,严格按照教科书上的规定,死扣E-R图和3NF。他们的作品,对于一般的access型轻量级的管理软件,已经够用。但是一旦该系统需要添加新功能,原有的数据库表差不多得进行大换血。
3、第二类程序员,在经历过数次程序效率的提升,以及功能升级的折腾后,终于升级成为数据库设计的老鸟,第一类程序员眼中的高人。这类程序员可以胜任二十个表以上的中型商业数据管理系统的开发工作。他们知道该在什么样的情况下保留一定的冗余数据来提高程序效率,而且其设计的数据库可拓展性较好,当用户需要添加新功能时,原有数据库表只需做少量修改即可。
4、在经历过上十个类似数据库管理软件的重复设计后,第三类程序员中坚持下来没有转行,而是希望从中找出“偷懒”窍门的有心人会慢慢觉悟,从而完成量变到质变的转换。他们所设计的数据库表结构有一定的远见,能够预测到未来功能升级所需要的数据,从而预先留下伏笔。这类程序员目前大多晋级成数据挖掘方面的高级软件开发人员。
一、树型关系的数据表
不少程序员在进行数据库设计的时候都遇到过树型关系的数据,例如常见的类别表,即一个大类,下面有若干个子类,某些子类又有子类这样的情况。当类别不确定,用户希望可以在任意类别下添加新的子类,或者删除某个类别和其下的所有子类,而且预计以后其数量会逐步增长,此时我们就会考虑用一个数据表来保存这些数据。按照教科书上的教导,第二类程序员大概会设计出这样的数据表结构
类别表_1(Type_table_1)
名称 类型 约束条件 说明 type_id int 无重复 类别标识,主键 type_name char(50) 不允许为空 类型名称,不允许重复 type_father int 不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值
这样的设计短小精悍,完全满足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_father int 不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值 type_layer char(6) 限定3层,初始值为000000 类别的先序遍历,主要为减少检索数据库的次数
按照这样的表结构,我们来看看上面例子记录在表中的数据是怎样的:
type_id type_name type_father type_layer 1 总类别 0 000000 2 类别1 1 010000 3 类别1.1 2 010100 4 类别1.2 2 010200 5 类别2 1 020000 6 类别2.1 5 020100 7 类别3 1 030000 8 类别3.1 7 030100 9 类别3.2 7 030200 10 类别1.1.1 3 010101 ……
现在按type_layer的大小来检索一下:
SELECT * FROM Type_table_2 ORDER BY type_layer
列出记录集如下:
type_id type_name type_father type_layer 1 总类别 0 000000 2 类别1 1 010000 3 类别1.1 2 010100 10 类别1.1.1 3 010101 4 类别1.2 2 010200 5 类别2 1 020000 6 类别2.1 5 020100 7 类别3 1 030000 8 类别3.1 7 030100 9 类别3.2 7 030200 ......
现在列出的记录顺序正好是先序遍历的结果。在控制显示类别的层次时,只要对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)
商品类型表(Wares_type) 名称 类型 约束条件 说明 type_id int 无重复 类别标识,主键 type_name char(50) 不允许为空 类型名称,不允许重复 type_father int 不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值 type_layer char(6) 限定3层,初始值为000000 类别的先序遍历,主要为减少检索数据库的次数
供货厂商表(Wares_provider) 名称 类型 约束条件 说明 provider_id int 无重复 供货商标识,主键 provider_name char(100) 不允许为空 供货商名称
商品信息表(Wares_info)
名称 类型 约束条件 说明 wares_id int 无重复 商品标识,主键 wares_name char(100) 不允许为空 商品名称 wares_type int 不允许为空 商品类型标识,和Wares_type.type_id关联 wares_info char(200) 允许为空 相关信息 provider int 不允许为空 供货厂商标识,和Wares_provider.provider_id关联 setnum int 初始值为1 内含件数,默认为1 stock int 初始值为0 库存,默认为0 buy_price money 不允许为空 进货价 sell_price money 不允许为空 销售价 discount money 不允许为空 优惠价
你拿着这3个表给老板检查,老板希望能够再添加一个商品图片的字段,不过只有一部分商品有图片。OK,你在商品信息表(Wares_info)中增加了一个haspic的BOOL型字段,然后再建了一个新表——商品图片表(Wares_pic):
商品图片表(Wares_pic) 名称 类型 约束条件 说明 pic_id int 无重复 商品图片标识,主键 wares_id int 不允许为空 所属商品标识,和Wares_info.wares_id关联 pic_address char(200) 不允许为空 图片存放路径
一段时间后,老板打算在这套平台上推出新的商品销售,其中,某类商品全部都需添加“长度”的属性。第一轮折腾来了……当然,你按照添加商品图片表的老方法,在商品信息表(Wares_info)中增加了一个haslength的BOOL型字段,又建了一个新表——商品长度表(Wares_length):
商品长度表(Wares_length) 名称 类型 约束条件 说明 length_id int 无重复 商品图片标识,主键 wares_id int 不允许为空 所属商品标识,和Wares_info.wares_id关联 length char(20) 不允许为空 商品长度说明
一段时间后,老板又打算上一批新的商品,这次某类商品全部需要添加“宽度”的属性
你是不是开始觉得你所设计的数据库按照这种方式增长下去,很快就能变成一个迷宫呢?那么,有没有什么办法遏制这种不可预见性,但却类似重复的数据库膨胀呢?我在阅读《敏捷软件开发:原则、模式与实践》中发现作者举过类似的例子:7.3 “Copy”程序。其中,我非常赞同敏捷软件开发这个观点:在最初几乎不进行预先设计,但是一旦需求发生变化,此时作为一名追求卓越的程序员,应该从头审查整个架构设计,在此次修改中设计出能够满足日后类似修改的系统架构。下面是我在需要添加“长度”的属性时所提供的修改方案:
去掉商品信息表(Wares_info)中的haspic字段,添加商品额外属性表(Wares_ex_property)和商品额外信息表(Wares_ex_info)2个表来完成添加新属性的功能。
商品额外属性表(Wares_ex_property) 名称 类型 约束条件 说明 ex_pid int 无重复 商品额外属性标识,主键 p_name char(20) 不允许为空 额外属性名称
商品额外信息表(Wares_ex_info)
名称 类型 约束条件 说明 ex_iid int 无重复 商品额外信息标识,主键 wares_id int 不允许为空 所属商品标识,和Wares_info.wares_id关联 property_id int 不允许为空 商品额外属性标识,和Wares_ex_property.ex_pid关联 property_value char(200) 不允许为空 商品额外属性值
在商品额外属性表(Wares_ex_property)中添加2条记录:
ex_pid p_name 1 商品图片 2 商品长度
再在整个电子商务平台的后台管理功能中追加一项商品额外属性管理的功能,以后添加新的商品时出现新的属性,只需利用该功能往商品额外属性表(Wares_ex_property)中添加一条记录即可。
四、简洁的批量m:n设计
碰到m:n的关系,一般都是建立3个表。但是,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个原则:
1、用户的整体需求。当用户更多的关注于,对数据库的规范记录按一定的算法进行处理后,再列出的数据。如果该算法可以直接利用后台数据库系统的内嵌函数来完成,此时可以适当的增加冗余字段,甚至冗余表来保存这些经过算法处理后的数据。要知道,对于大批量数据的查询,修改或删除,后台数据库系统的效率远远高于我们自己编写的代码。
2、简化开发的复杂度。现代软件开发,实现同样的功能,方法有很多。尽管不必要求程序员精通绝大部分的开发工具和平台,但是还是需要了解哪种方法搭配哪种开发工具的程序更简洁,效率更高一些。冗余数据的本质就是用空间换时间,尤其是目前硬件的发展远远高于软件,所以适当的冗余是可以接受的。不过我还是在最后再强调一下:不要过多的依赖平台和开发工具的特性来简化开发,这个度要是没把握好的话,后期维护升级会栽大跟头的。
发表评论
-
SQL Server中临时表与表变量的区别
2011-04-20 10:01 1191转载:http://hi.baidu.com/windy ... -
如何充分利用索引
2011-04-20 09:28 1628转载:http://hi.baidu.com/yf ... -
Oracle Redo log并行机制详解
2011-04-20 01:31 1657转载:http://database.51cto.co ... -
Oracle的REDO和UNDO
2011-04-20 01:10 23814转载:http://blog.csdn.net/int ... -
数据库索引技术
2011-04-18 10:53 1542转载: 一、 什么是索 ... -
Mysql、SqlServer、Oracle主键自动增长的设置
2011-04-12 00:49 17791、把主键定义为自动增长标识符类型 在mysql中,如果把表 ... -
Spring+Hibernate框架下Mysql读写分离、主从数据库配置(下)
2011-04-08 23:17 2937转载:http://lujia35.iteye.com/blo ... -
Spring+Hibernate框架下Mysql读写分离、主从数据库配置(上)
2011-04-08 23:10 7853转载:http://lujia35.iteye.com/blo ... -
数据库调优涉及哪些方面
2011-04-08 23:08 966以影响程度排序 D1 业务逻辑 (影响最大)D2 ... -
SQL的书写规范
2011-04-08 22:29 1716转载:http://liriguang.iteye.c ... -
分布式可扩展数据库架构
2011-04-08 22:28 1796转载:http://liriguang.iteye.com/b ... -
数据库的垂直划分和水平划分
2011-04-08 21:37 1497转至:http://liriguang.iteye.c ... -
Oracle 常用SQL技巧
2010-11-17 11:37 895转载:http://www.iteye.com/topic/8 ... -
数据库的设计原则与开发技巧
2010-11-16 15:31 997随着计算机技术越 ... -
数据库三范示
2010-11-16 14:34 1601一、设计范示 在创建一个数据库的过程中,范化是将其转化为一 ... -
DB设计的常见问题
2010-10-24 16:11 1121转载:http://www.blogjava.net/rela ... -
为oracle创建用户
2009-12-25 10:06 2636Oracle安装完后,其中有 ...
相关推荐
### 数据库设计技巧经验浅析 #### 一、引言 数据库设计是软件开发中至关重要的环节,良好的数据库设计不仅能确保数据的有效存储与快速检索,还能为后续的应用扩展打下坚实的基础。根据“3分技术,7分经验”的原则...
浅谈数据库设计技巧.mht
通过学习和实践这些数据库设计技巧,程序员能够更好地应对各种复杂的业务场景,构建出高效、可靠的数据存储系统。在实际工作中,不断总结经验,持续学习新的数据库技术和最佳实践,是保持竞争力的关键。
老程序员工作笔记,五年以上的开发人员在工作中积累的经验,可以帮组很多新手,你值得拥有。
### 数据库设计技巧详解 #### 一、引言与背景 数据库设计是现代软件开发中的核心环节之一。本文从数据结构的重要性入手,探讨了不同类型的程序员在设计数据库时的表现,并提出了针对特定场景的设计技巧。 #### 二...
【数据库设计技巧】 数据库设计是IT领域中至关重要的一部分,它涉及到如何有效地组织和存储数据,以便于高效地访问和管理。本文将探讨一些数据库设计的关键技巧,尤其关注SQL语句的使用。 首先,数据结构是数据库...
【Oracle 数据库表设计技巧】 在Oracle数据库设计中,表的构建是至关重要的基础工作,对数据库性能的影响深远。良好的表设计能显著提升数据库效率。本文主要针对Oracle初学者,介绍一些实用的设计技巧。 首先,...
一个良好的数据库设计不仅需要满足数据存储的需求,还要保证数据的完整性和准确性,同时也要兼顾系统的查询速度和执行效率。在设计过程中,应考虑到应用需求和开发团队的技术能力,合理选择开发工具和开发环境。...
本文将从多个角度探讨数据库设计的方法和技巧,以期望对数据库设计人员提供指导和帮助。 设计前的准备工作是数据库设计的第一步。这一步骤包括对现有数据库设计环境的研究,了解客户的需求,对数据库运行状况的检查...
这些知识点不仅涵盖了SQL Server数据库的核心功能和技术,还包括了数据库设计、开发和维护的各个方面,对于任何希望深入理解和应用SQL Server数据库技术的专业人士而言,都是不可或缺的参考资料。
同时,优化工作需要在数据库设计阶段就开始考虑,以最小的代价获得最大的性能收益。在实际工作中,DBA和资深程序员应当利用他们的经验,通过分析SQL语句的执行计划,不断尝试优化方案,最终实现查询效率的最大化。
"浅谈海量数据处理技巧" 本文主要讨论海量数据处理的技巧,包括数据导入、数据库设计、索引创建和分表存储等方面。 数据导入是海量数据处理的第一步。文中提到三种将文本数据导入 SQL Server 数据库的方式:通过...
### DB2 数据库调优浅谈 #### 数据库调优的视角 在进行数据库调优时,不同的视角可能会带来不同的解决方案。通常来说,可以从以下几个方面考虑: - **应用优化**:针对应用程序本身的优化,比如查询逻辑、事务...
例如,通过创建班级信息数据库的案例,引导学生思考数据库的构建方法,以及如何设计数据库表、视图、索引等。而任务驱动法则强调在完成具体任务的过程中,将理论与实践相结合,通过实际操作来提高学生解决问题的能力...
这些技巧如果能够在数据库应用开发中恰当运用,将有可能为数据库性能带来显著的提升,甚至带来意外的惊喜。 文章编号为1002-8331,文献标识码为A,中图分类号为TP311.13。在结尾部分,文章提供了一个样本分析,即...
数据库优化是提升系统性能的关键环节,...综上所述,数据库优化涵盖了表分区、别名、索引、物化视图、事务管理和I/O优化等多个方面。根据实际的业务需求和数据库特性,灵活运用这些策略,才能实现最佳的数据库性能。