级联数据库表
在日常的软件设计中,多级联动的数据库表比较常见,如省市联动的数据,对这些数据按级分表的方式存储,以表示数据之间的包含关系。
一般情况下,不会使用外键将父、子级的数据库表进行关联,因为当需要对某个数据进行删除操作时,这个数据就需要数据库级联操作来去除对子表的影响,而这些操作的开销会很大(级数越多,开销越大)。
而解决这个问题的常用方法,就是在子级表中添加一个字段,存入对应父级表的id值,类似链表。
此设计,便于增加和更新操作,减小各级的耦合,类似于链表的结构,可以最大程度的减少对数据的修改。但空间的优化,导致的缺点毫无疑问的就是时间的牺牲。即当需要通过子级查询更上级的父级数据时,需要发起多次的查询。(子→父→爷)
例如: 通过一个商品,查询它所属的一级分类?(商品→N级分类…→三级分类→二级分类→一级分类)
如何快速通过子级查询父级?
下面进行一个粗糙的实验,使用SSM框架+MySQL+Junit,进行四级的级联查询(四级分类→三级分类→二级分类→一级分类),下例中均以一级分类作查询最终目标。
1.目前最原始的结构如下:
为了方便这里简化数据表的结构,对其重复进行10000次的查询,通过System.currentTimeMillis();记录时间差,当然由于Mybatis的一级缓存,使得这样重复查询的耗时、访问次数减少,这里粗糙地表现数据库的访问次数。
while (rount>=0){
//实际上向数据库发起了4次查询
FouthL fouth = dao.findFouth(1);
ThirdL third = dao.findThird(fouth.getThird_id());
SecondL second = dao.findsecond(third.getSec_id());
FirstL first = dao.findFirst(second.getFir_id());
//模拟提取数据
first.getFir_data();
}
输出:
2.常见的解决方法:
在子级数据库表中为每一个父级的id添加字段,即子级拥有所有父级的id信息,这样只需要2次查询即可实现子级查询父级,但随着级联的层数增大,空间的占用越大,浪费越大。
while (rount>=0){
//实际上向数据库发起了2次查询
FouthL fouth = dao.findFouth(1);
FirstL first = dao.findFirst(fouth.getFir_id());
first.getFir_data();
}
由于Mybatis的一级缓存,这里的耗时差距并不明显,但数据库的访问量极大的减少了。
3.个人的一些修改方式
当我们能确定每一级数据的数据量和级数时
如:这里的数据只有4级,每个父级的数据不超过1000个
我们可以将多个父级id字段进行合并,以500055001表示id为500,55,1的一级,二级,三级分类的id值(这里不使用varchar类型,是因为拿到数据后,切割字符串会造成内存的大量浪费)
这样减少数据库的空间浪费,通过代码运算将字段拆分回多个父级的id,但牺牲了部分CPU性能。
while (rount>0){
//实际上向数据库发起了2次查询
FouthL fouth = dao.findFouth(2);
BigInteger parentId = fouth.getParent_id();
//分解出各级id
//这里对应万位为一级分类的id,百位~千位为二级id,十位~个位为一级id
int fir_Id = parentId.divide(new BigInteger("10000")).intValue();
int sec_Id = parentId.divide(new BigInteger("100")).mod(new BigInteger("100")).intValue();
int third_Id = parentId.mod(new BigInteger("100")).intValue();
//查询首级分类
FirstL first = dao.findFirst(fir_Id);
first.getFir_data();
}
最后,各个方案都有优缺点,具体需要根据软件的设计需求来决定。
作为一个初出茅庐的新人菜鸟,文中有不当之处,还请各位多多指正!!