自定义排序之数据库设计
之前做过的项目有项需求,就是要对一个普通的列表进行自定义排序功能,当初构思了几个方案,各有所长,按需使用,下面就一一来介绍这几个方案。
注:这里的自定义排序就是操作列表的某项进行位置交换。
1. 单表单列结构(数组结构)
此设计是使用一个表中的一列来表示数据的序号,通常我们使用的方法就是这种。
数据表tb_data(n):
data | index |
---|---|
··· | 0 |
··· | 1 |
··· | 2 |
这里规定序号从0开始递增。
其基本数据操作如下:
- 增 → 当增加一个数据时,定义data的序号为数据总数量值。
- 删 → 当删除一个数据时,将大于该序号的数据的序号都减1。
- 改 → 当修改位置将数据a从x移动到y时,若x小于y,则将(x,y]范围内的数据序号都减1;若x大于y,则将[y,x)范围内的数据序号都加1(注:修改数据库时,要先将a的序号x修改为未被使用的序号z,然后再修改范围内的数据,最后再将z修改为y,顺序不能乱)。
- 查 → 当查找数据时添加 order by index 即可得到自定义排列的数据,查找第n个数据时查找条件为 index=n-1 即可。
总结:此方法查找速度最快,修改速度最慢。
2.单表双列结构(链表结构)
此设计是使用一个表中的两列来表示数据的序号,一列表示该数据的前置id,一列表示该数据的后置序id(id为数据表本身的自增序号),即相当于我们经常使用的双向链表。
数据表tb_data(n):
id | data | pre_no | next_no |
---|---|---|---|
0 | ··· | -1 | 1 |
1 | ··· | 0 | 2 |
2 | ··· | 1 | 3 |
3 | ··· | 2 | -1 |
这里规定第一个数据的pre_no为-1,最后一个数据的next_no为-1。
其基本数据操作如下:
- 增 → 当增加一个数据a时,先查找出最后一个数据b的id号,a的pre_no定义为b的id号(若此数据为第一个则定义为-1),next_no定义为-1,再将b的next_no定义为a的id号。
- 删 → 当删除一个数据a时,取出a的pre_no和next_no,将pre_no对应的id的数据的next_no修改为a的next_no,将next_no对应的id的数据的pre_no修改为a的pre_no。
- 改 → 令位置x-1、x+1数据分别为b、c,位置y-1、y、y+1的数据分别为d、e、f,现修改位置将数据a从x移动到y时,
当x小于y时,修改b的next_no为c的id,修改c的pre_no为b的id,修改e的next_no为a的id,修改f的pre_no为a的id,最后修改a的pre_no为e的id,next_no为f的id;
当x大于y时,修改b的next_no为c的id,修改c的pre_no为b的id,修改d的next_no为a的id,修改e的pre_no为a的id,最后修改a的pre_no为d的id,next_no为e的id。 - 查 → 当查找第n个数据时,需要先查找出第一个数据,在根据第一个数据逐个往后查找数据的next_no,查找n次后得到第n个数据。
总结:此方法查找速度最慢,修改速度最快。
3.双表双列结构(分页结构)
此设计是使用一个页码表记录全部页码和页码的序号范围,另一个数据表来记录基本数据、自身序号和页码,通过在一个表中给数据设置不同的序号和页码来达到分页排序的效果。
页码表tb_page:
tb_name | page | start_index | end_index |
---|---|---|---|
tb_data0 | 0 | 1 | 1000 |
tb_data0 | 1 | 1 | 1000 |
tb_data0 | 2 | 1 | 120 |
tb_data1 | 0 | 1 | 1000 |
tb_data1 | 1 | 1 | 130 |
table_name为一个基本数据表的表名,对于每一个table_name,其对应的page都从0开始递增且不能重复,每个page有一定的数据范围(这里设定为一页有1000条数据),则每个数据表对应的数据总量即可计算出来。
基本数据表tb_data(n):
data | page | index |
---|---|---|
··· | 0 | 1 |
··· | 0 | ··· |
··· | 0 | 1000 |
··· | 1 | 1 |
··· | 1 | ··· |
··· | 1 | 1000 |
··· | 2 | 1 |
··· | 2 | ··· |
··· | 2 | 120 |
data为数据域,记录基本数据,对于每一个不相同的data都有对应的page和index,通过page、index和每一页的数据范围即可计算出对应的全局序号。
其基本数据操作如下:
- 增 → 当增加一个数据时,先从页码表中查找出最后的page及其对应的start_index和end_index,若end_index-start_index+1没有数据超出范围,则插入数据的page不变,index为end_index+1,若超出范围,则需在页码表中新建一页,插入数据的page自加一,index赋值初始值。
- 删 → 当删除一个数据时,因为需要保证除末页以外的页码的数据范围都为规定值,所以需要对删除数据对应的页码及以上页码的数据进行调整,对于删除数据的页码,可以通过对比该数据在页码中的相对位置来调整较小数据量的一方数据执行加减1,对于大于其的页码,可以将每一页的首数据调整为上一页的末数据(末页数量为0即可将末页删除)。
- 改 → 当修改位置将数据a从x移动到y,计算x、y对应的页码,当x、y的页码相同时,只需调整该页数据,可通过对比x到y的数量和页码数量来决定调整x、y范围内还是范围外的数据信息;当x、y页码不同时,可通过对x、y在页码中的相对位置来调整较小数据量的一方数据执行加减1,若x小于y,(x,y]范围内的页的首数据调整为上一页的末数据,若x大于y,[y,x)范围内的页的末数据调整为下一页的首数据。
- 查 → 当查找数据总量时,可以通过查找页码表计算得出;当查找序号为n的数据时,可以通过页码表计算得到对应的page和index,然后再通过查找数据表取得数据(由于页码表的数据会经常使用,所以最好从数据库取出一次后保存在内存中再进行使用即可提高速度)。
总结:此方法查找速度和修改速度比较均衡,适合大多数情况使用。