前言
我们在碰到类似于组织架构的数据时,往往避免不了多分级的树形结构,分级会带来查询效率的问题,尤其是分级越深,递归效率越慢,页面要等待很久才能拿到数据。
这里提供一种针对稳定数据的树形结构设计方案——左右值。
以银行结构为例,传统表格设计方案为:
|id|名称|地址|父级id|级别|
id | name | address | parent_id | level |
---|---|---|---|---|
10 | X银行总行 | X省 | 0 | 1 |
1001 | X银行京分行 | X省京市 | 10 | 2 |
1002 | X银行沪分行 | X省沪市 | 10 | 2 |
100101 | X银行昌平支行 | X省京市昌平区 | 1001 | 3 |
10010101 | X银行昌平支行和平路网点 | X省京市昌平区和平路 | 100101 | 4 |
对于上述的结构,我们在查询完整的树形结构时需要进行深度递归,这样的效率会很慢导致页面长时间加载。
左右值方案
表格设计
|id|名称|地址|级别|左值|右值|
id | name | address | level | left | right |
---|---|---|---|---|---|
10 | X银行总行 | X省 | 1 | 1 | 24 |
1001 | X银行京分行 | X省京市 | 2 | 2 | 15 |
1002 | X银行沪分行 | X省沪市 | 2 | 16 | 23 |
100101 | X银行昌平支行 | X省京市昌平区 | 3 | 3 | 8 |
100102 | X银行朝阳支行 | X省京市朝阳区 | 3 | 9 | 14 |
100201 | X银行黄埔支行 | X省沪市黄埔区 | 3 | 17 | 18 |
100202 | X银行徐汇支行 | X省沪市徐汇区 | 3 | 19 | 22 |
10010101 | X银行昌平支行A网点 | X省京市昌平区A路 | 4 | 4 | 5 |
10010102 | X银行昌平支行B网点 | X省京市昌平区B路 | 4 | 6 | 7 |
10010201 | X银行朝阳支行C网点 | X省京市朝阳区C路 | 4 | 10 | 11 |
10010202 | X银行朝阳支行D网点 | X省京市朝阳区D路 | 4 | 12 | 13 |
10020201 | X银行徐汇支行G网点 | X省沪市徐汇区G路 | 4 | 20 | 21 |
在表格中我们使用了左右值替换了父级id字段,因为在左右值的设计中不需要父级id字段来维护上下级关系。
树状图
如图所示,我们给每一个节点标注左值和右值。不难发现,当我们查询子节点时只需要查询左值大于当前结点左值并且右值小于当前结点右值的节点即可,即子节点左右值在当前节点的左右值区间内。
以京行为例,以 left >2 and right<15 为条件,即可获取所有的子节点。另外配上数据层级 level即可查询指点级别的子节点。如以 left >2 and right<15 and level=3 为条件,即可获得昌平支行和朝阳支行两条数据。
如此一来,数据查询不需要递归,大大增加了查询的效率。
增加节点
如上图所有增加节点时,将新增加的子节点作为该节点的最后一个子节点,前面节点左右值不变,父节点右值 +2 且大于父节点右值的左右值均 +2。反之,删除节点即将大于当前右值的节点左右值均 -2。
总结
通篇下来,该方案的优点在于易查询,不借助递归,能很快得到树状数据。但是也有一个很明显的缺点,增删操作时需要连带后面节点的左右值数据一起修改,不适用于频繁增加或删除的业务场景。所以我在开篇强调这是一个针对于稳定数据的树形结构设计方案。总而言之,没有最好的设计方案,只有最合适的设计方案,大家根据场景取舍。