刚学编程不久时,曾参加了一个短期培训。培训结束时,和毕业设计一样,要做一个模拟项目,几个人一起的。我选的是一个在线音乐网站,当时觉得自己水平已经不错了,虽然小组其他人比较菜,做出来应该问题不大。但在一个音乐收藏夹目录的数据库设计上,我和我们老师有一次可以说比较激烈的争吵。

  我的想法是,每个目录应该包含多个子目录,但最多只有一个父目录,应该只有一种最简单、最合理选择:每个目录有唯一主键及父目录主键。

  

  我的想法却被指导老师否定了,他当时讲的理由是什么,我几乎全忘了,可能根本没在听。我坚决地维护自己观点,辩论中举出二叉树、数据库范式等种种理论。我并不需要什么证书,不怕得罪谁,我的数据库系统工程师证书应该更有含金量。当然最后,谁也没有被说服。

  转眼三年过去了,今天的自己在这里写此文,证明当初老师是正确的,自己是无知的。更合理的实现方式是:每个目录的唯一主键只作物理层,不用于逻辑层,也不保存父目录ID,取代的是一个"目录编号"的字段。

  目录编号是唯一的,属于字符串(VarChar)类型,它可以被修改,不同于主键。关键一点是:某目录的编号去掉最后N位,就是父目录的编号。N取决于任一目录下最多的子目录数目,超出100个子目录的情况很少碰到,因此一般为2足够。比如下表:

主键 编号 名称
- 00 中国
- 0000 北京
- 0001 山东
- 000100 青岛

  这种方式做相对有什么好处呢?这可以消除额外的数据库连接操作,大大提高基于目录层次的查询效率,而且非常灵活,可以自由筛选目录层级。这也是被各大门户网站所采用的频道分类的方式。目录、频道、地域、部门等所有树状结构,都可以采用这种方式保存。

  当然,有利就有弊,这种方式的问题在于增删改时,编程工作量大一些,系统开销也较大。不过,性能上这不算问题,毕竟增删改操作不常发生。因为目录这样的数据需要被缓存,所以还应考虑缓存同步。下面就简单说一下不同操作的思路,其关键是就是编号字段。

  一、查找子目录

  子目录的编号,一定包含父目录编号,而且是从第一位开始,不管是其下面第几层子目录。如果要限制层级,比如只找一级子目录,限制子目录编号位数即可。代码上实现,如探囊取物耳。

  二、查找目录下元素

  比如部门下的员工,栏目下的文章等。完全不需要表连接操作,查找目录编号字段从第一位包含待查询的目录编号的所有记录即可。SQL或LINQ to SQL也同样简单。

  三、添加操作

  1. 首先根据其上级目录编号,找到其现有全部子目录,取每个子目录编号后两位,形成一个100内的整数集合。如果没有上级目录,则上级目录编号为Empty。

  2. 根据得到的子目录识别符集合,获取一个未被使用的识别符,长为两位字符串。

  3. 上级目录编号与新目录识别符拼接,连成新目录编号。

  四、修改操作

  改其他字段没什么问题。如果要修改上级目录,要走以下步骤:

  1. 假设此目录叫X,找到X的所有层级的子目录。

  2. 按照添加操作的步骤,为X生成在新的上级目录下的新目录编号。

  3. 将其所有子目录的目录编号中包含的X的原编号,替换为新编号。

  4. 将每个编号修改过的目录下的元素,旧编号替换为新编号。

   五、删除操作

  1. 查找所有目录下元素,如果集合不为空,可根据业务逻辑,将所有元素删除,或取消操作。

  2. 查找所有子目录并删除。

  高效目录存储结构的基本操作便是这五种。不知道我写得是不是清楚,不过无论怎么说,这种模式的美感要在实践应用中才能体会。在下文中,将描述其B/S项目中的具体应用。

  在此,要向我的指导老师-刘金钢为当年的顶撞表示诚挚歉意,不管他能否看到这篇文章。

posted on 2011-04-13 21:55  小城故事  阅读(4754)  评论(22编辑  收藏  举报