说到数据库我认为不能不先谈数据结构年在我初入大学学习计算机编程时当时的老师就告诉我们说计算机程序=数据结构+算法尽管现在的程序开发已由面向过程为主逐步过渡到面向对象为主但我还是深深赞同年前老师的告诉我们的公式计算机程序=数据结构+算法面向对象的程序开发要做的第一件事就是先分析整个程序中需处理的数据从中提取出抽象模板以这个抽象模板设计类再在其中逐步添加处理其数据的函数(即算法)最后再给类中的数据成员和函数划分访问权限从而实现封装
数据库的最初雏形据说源自美国一个奶牛场的记账薄(纸质的由此可见数据库并不一定是存储在电脑里的数据^_^)里面记录的是该奶牛场的收支账目程序员在将其整理录入到电脑中时从中受到启发当按照规定好的数据结构所采集到的数据量大到一定程度后出于程序执行效率的考虑程序员将其中的检索更新维护等功能分离出来做成单独调用的模块这个模块后来就慢慢发展演变成现在我们所接触到的数据库管理系统(dbms)——程序开发中的一个重要分支
下面进入正题首先按我个人所接触过的程序给数据库设计人员的功底分一下类
1没有系统学习过数据结构的程序员这类程序员的作品往往只是他们的即兴玩具他们往往习惯只设计有限的几个表实现某类功能的数据全部塞在一个表中各表之间几乎毫无关联网上不少的免费管理软件都是这样的东西当程序功能有限数据量不多的时候其程序运行起来没有什么问题但是如果用其管理比较重要的数据风险性非常大
2系统学习过数据结构但是还没有开发过对程序效率要求比较高的管理软件的程序员这类人多半刚从学校毕业不久他们在设计数据库表结构时严格按照教科书上的规定死扣er图和nf(别灰心所有的数据库设计高手都是从这一步开始的)他们的作品对于一般的access型轻量级的管理软件已经够用但是一旦该系统需要添加新功能原有的数据库表差不多得进行大换血
3第二类程序员在经历过数次程序效率的提升以及功能升级的折腾后终于升级成为数据库设计的老鸟第一类程序员眼中的高人这类程序员可以胜任二十个表以上的中型商业数据管理系统的开发工作他们知道该在什么样的情况下保留一定的冗余数据来提高程序效率而且其设计的数据库可拓展性较好当用户需要添加新功能时原有数据库表只需做少量修改即可
4在经历过上十个类似数据库管理软件的重复设计后第三类程序员中坚持下来没有转行而是希望从中找出偷懒窍门的有心人会慢慢觉悟从而完成量变到质变的转换他们所设计的数据库表结构有一定的远见能够预测到未来功能升级所需要的数据从而预先留下伏笔这类程序员目前大多晋级成数据挖掘方面的高级软件开发人员
5第三类程序员或第四类程序员在对现有的各家数据库管理系统的原理和开发都有一定的钻研后要么在其基础上进行二次开发要么自行开发一套有自主版权的通用数据库管理系统
我个人正处于第三类的末期所以下面所列出的一些设计技巧只适合第二类和部分第三类数据库设计人员同时由于我很少碰到有兴趣在这方面深钻下去的同行所以文中难免出现错误和遗漏在此先行声明欢迎大家指正不要藏私哦)
一树型关系的数据表
不少程序员在进行数据库设计的时候都遇到过树型关系的数据例如常见的类别表即一个大类下面有若干个子类某些子类又有子类这样的情况当类别不确定用户希望可以在任意类别下添加新的子类或者删除某个类别和其下的所有子类而且预计以后其数量会逐步增长此时我们就会考虑用一个数据表来保存这些数据按照教科书上的教导第二类程序员大概会设计出类似这样的数据表结构
类别表_(type_table_)
名称类型约束条件说明
type_id int 无重复 类别标识主键
type_namechar() 不允许为空 类型名称不允许重复
type_father int 不允许为空 该类别的父类别标识如果是顶节点的话设定为某个唯一值
这样的设计短小精悍完全满足nf而且可以满足用户的所有要求是不是这样就行呢?答案是no!why?
我们来估计一下用户希望如何罗列出这个表的数据的对用户而言他当然期望按他所设定的层次关系一次罗列出所有的类别例如这样
总类别
类别
类别
类别
类别
类别
类别
类别
类别
类别
……
看看为了实现这样的列表显示(树的先序遍历)要对上面的表进行多少次检索?注意尽管类别可能是在类别之后添加的记录答案仍然是n 次这样的效率对于少量的数据没什么影响但是日后类型扩充到数十条甚至上百条记录后单单列一次类型就要检索数十次该表整个程序的运行效率就不敢恭维了或许第二类程序员会说那我再建一个临时数组或临时表专门保存类型表的先序遍历结果这样只在第一次运行时检索数十次再次罗列所有的类型关系时就直接读那个临时数组或临时表就行了其实用不着再去分配一块新的内存来保存这些数据只要对数据表进行一定的扩充再对添加类型的数量进行一下约束就行了要完成上面的列表只需一次检索就行了下面是扩充后的数据表结构
类别表_(type_table_)
名称类型约束条件说明
type_id int无重复类别标识主键
type_name char()不允许为空类型名称不允许重复
type_father int 不允许为空该类别的父类别标识如果是顶节点的话设定为某个唯一值
type_layer char()限定层初始值为类别的先序遍历主要为减少检索数据库的次数
按照这样的表结构我们来看看上面例子记录在表中的数据是怎样的
type_id type_name type_father type_layer
总类别
类别
类别
类别
类别
类别
类别
类别
类别
类别
……
现在按type_layer的大小来检索一下select * from type_table_ order by type_layer
列出记录集如下
type_id type_name type_father type_layer
总类别
类别
类别
类别
类别
类别
类别
类别
类别
类别
……
现在列出的记录顺序正好是先序遍历的结果在控制显示类别的层次时只要对type_layer字段中的数值进行判断每位一组如大于则向右移 个空格当然我这个例子中设定的限制条件是最多层每层最多可设个子类别只要按用户的需求情况修改一下type_layer的长度和位数即可更改限制层数和子类别数其实上面的设计不单单只在类别表中用到网上某些可按树型列表显示的论坛程序大多采用类似的设计
或许有人认为type_table_中的type_father字段是冗余数据可以除去如果这样在插入删除某个类别的时候就得对 type_layer 的内容进行比较繁琐的判定所以我并没有消去type_father字段这也正符合数据库设计中适当保留冗余数据的来降低程序复杂度的原则后面我会举一个故意增加数据冗余的案例
二商品信息表的设计
假设你是一家百货公司电脑部的开发人员某天老板要求你为公司开发一套网上电子商务平台该百货公司有数千种商品出售不过目前仅打算先在网上销售数十种方便运输的商品当然以后可能会陆续在该电子商务平台上增加新的商品出售现在开始进行该平台数据库的商品信息表的设计每种出售的商品都