数据库 模型结构设计的优化

一个表结构的设计,不应当是单纯地从对象设计地属性中一一映射而来,有些时候稍微修改一下设计思路,就可以让程序变得简单,而且问题容易解决。下面举个例子来说明一下。

当尝试通过二维表来表达一个树地机构时,通常会用一个parent_id来代表父亲节点地ID,这样就能在逻辑上串联起来,表结构如下:

id name parent_id
1  xxx  null
2  xxx  1
3  xxx  1
4  xxx  2
5  xxx  2

但是当需要通过某个节点来访问所有子结点或父亲节点时间问题就麻烦了,要实现这个功能可能会写很多代码,当然部分数据库提供了递归方法(例如Oracle地start with connect by prior语法),但是这种方法有个毛病,就是在向下递归的过程中,每一层的每一个子节点越来越多(例如达到10万个节点)时,访问将会变得很慢,数据量继续上升会更慢。向上递归是没有问题的,因为在树结构中父亲节点最多只有一个。

有这样大的一棵树?当然有!想想全国地区从省、市、县到区、镇、乡、村、门派号等是多么大的一棵树。难道这种问题无界吗?当然不是,我们并不一定非要和技术较劲,可以通过设计手段避开一些问题。

我们开始幻想:假如在表中设计一个字段,它能保存自己所有的父亲节点路径上的ID呢?因为这样问题将迎刃而解,于是表结构变成如下状况:

id name parent_id all_parent_id
1  xxx  null      {1}
2  xxx  1         {1}{2}
3  xxx  1         {1}{3}
4  xxx  2         {1}{2}{4}
5  xxx  2         {1}{2}{5}
6  xxx  3         {1}{3}{6}

程序中从创建这一棵树的根节点开始,all_parent_id将作为树节点的一个属性,根节点的all_parent_id就是{id}本身,其子节点肯定是基于父亲节点创建的,因此创建子节点需要生成的的all_parent_id,根据父亲节点的all_parent_id+{自身id}便可以得到。在这个问题上,我们可以发现层次不会过多,7、8层就是十分恐怖的了,所以这个字段宽度也不会太宽,可以建立一个索引。

如果要查询这个节点的所有父亲节点,自然的根据这个字段拆分出all_parent_id中的数字,根据主键ID就能过滤出来,而由于层次不会太多,因此拆分出来的ID值也不会太多,所以这里通常都会走索引。

如果要查询这个节点的所有子节点,则将当前节点的all_parent_id后缀拼接一个%,使用where all_parent_id like ?就可以检索出来,很显然是前缀匹配,所以通常会走索引。而我们很少会从根节点去查询下面所有的子节点,而且返回的数据也会有所限制,所以它通常是高效的,而且是十分简单的。

另外,如果按照这个字段排序,它就会按照一种树的层次结构进行展现,而它本身又是在索引上,所以是有序的结构,效率上也不会存在太大的问题。

总而言之,有些时候实际场景可能与我们的这种假设有所区别,此时希望大家加以变通来完成。

编码方式的例子远不止如此,为此我们再讲讲“编码规则“,它与上述案例类似,但是它不需要额外的字段来存储一个all_parent_id,因为它的ID上本身就有父亲节点的信息。

例如车牌号、身份证号都会有类似的设计,它们每个部分的数据都标示一个维度(例如身份证号前6位代表地址,中间8位代表出生年月日等),如果将这个维度变换为层次,就可以设计出树的结构。假如一个数据分解为3个层次,第一个层次最多几十条数据,那么只需要2位十进制数字即可表达,后面两层中每一层需要表达几万种可能性,那么每一层需要5位数字表达,那么自然的可以用定长12个十进制位来表达即可。此时的编码就像这样:

02 00001 00131
11 00033 89121
45 13312 13213

数据之间的空格只是为了大家看数据,实际存储进去是没有空格的。

这种数据如果用字符串来存放,那么使用方式就像all_parent_id一样(注意传入的参数隐式不走索引),如果使用数字存储,第一层次的01-09这类数字在程序种使用时要稍加处理,否则不足12位,这一层也可以考虑从10开始。

如果用数字存储,那么使用方式做一些转变,假如要查询出以02为前缀的所有编码,则使用between 20000000000 and 29999999999,自然的如果要求以11 00123为前缀的所有编码,就使用between 110012300000 and 110012399999即可。

从这些道理上大家是否体会到设计给我们带来的好处,同时还有一种真正玩转数字的享受,在实际的业务场景中通过设计手段解决问题的例子非常多。这些内容可以说与技术没有多大的关系,但是确实很重要,与我们的思维习惯有关,在解决问题的过程中一定不要仅仅局限一种思维方式。我们要多去看看别人是如何解决问题的,多学习别人的方法和思路,而不仅仅是一种语法和技术,这些思维凡事其实很多时候来源于生活场景,需要我们多思考。

总而言之,如果在一个系统中做一些常规的业务操作,需要十分复杂的操作与运算才能到达目的,而且这些复杂的逻辑运算是极度消耗性能的,那么可以考虑,是不是可以改一改设计。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值