经典问题,刚好以前解决过,分享一下: 就是逻辑树,怎么存储的问题。
主要考虑点: 无限层次扩展 和 查询性能 矛盾的解决。
基本模式:
表:Location
编码 | 名称 | 父编码 |
CN | 中国 | |
ZJ | 浙江省 | CN |
HZ | 杭州市 | HZ |
XH | 滨江区 | HZ |
NB | 宁波市 | ZJ |
优点: 模型简约而不简单, 支持任意层次扩展。
缺点:查询一个节点的所有子节点需要递归,效率不高。
一般解决: Oracle 中有这个语句可以解决查询问题 select * from XXX start with id=76 connect by prior parentid=id。 但是数据量大时效率有影响。
更好的解决:用好Cache。 一般这类数据都比较固定,而且量不会非常大,使用Cache,无论是程序递归查询,还是 connect by prior 语句,问题都不大。
索引表支持
如果属性结构数据量比较大或变动频繁, 应用比较关注性能,可以增加一个索引表来解决
行政区划索引表 ( Location_Index ) ( 主键:ID, PID)
编码 | 上线编码(非父级(PID)) | 相差存次(offset) |
CN | ||
ZJ | CN | 1 |
HZ | CN | 2 |
HZ | ZJ | 1 |
XH | CN | 3 |
XH | ZJ | 2 |
XH | HZ | 1 |
这张索引表以扁平化结构存储任意层次两个节点间的关系。检索效率很高:
1.查询任意节点的子节点: select * from Location_Index where PID = ‘ZJ’;
2.查询任意节点的所有父节点: select * from Location_Index where ID = ‘XH’;
3.判断任意两个节点是否在同一分支上: select * from Location_Index where ID = ‘XH’ and PID = ‘CN’;
当然,高性能是以空间和处理复杂度为代价的。
a) 新增一个节点时:除了要在 Location 表中增加一条记录, 同时要在 Location_Index 表增加 若干条记录。
b) 删除一个节点是,除了要在 Location 表中增加一条记录, 同时要在 Location_Index 表删除 若干条记录。
当然,这个逻辑并不复杂。而且 Location_Index 所增加的空间也是值的的。原来的 0001.0001这样的索引可以不需要,也能节省很多空间。
总结:
1) 表结构:基本表和索引表, Location,Location_Index。
2) 特点:支持无限层次节点的树结构;
查询效率很高。