这段时间遇到一个需求,就是对列表数据进行自定义排序。一开始的想法是数据库中增加一个排序字段,比如:
| id | name | sort_num |
|---|---|---|
| 1 | 张三 | 1 |
| 2 | 李四 | 2 |
| ... | ... | ... |
| 1000 | 王五 | 1000 |
当要调整选项的顺序时,则调整对应选项的sort_num。但这样有个问题,就是修改很慢。比如要把id为1000条数据的排序放在最前面,就首先需要把他的sort_num改为1,然后把id为1到999的所有数据的sort_num依次加1。
后面我们对上面做了一点改进。在存储sort_num时不再是加1,而是增加一个固定步长,当需要移动排序时,新的sort_num取目标位置相邻两个数的中间值。假设上述数据我们设置固定步长为1024,则变为:
| id | name | sort_num |
|---|---|---|
| 1 | 张三 | 1024 |
| 2 | 李四 | 2048 |
| ... | ... | ... |
| 1000 | 王五 | 1024000 |
现在移动的方案变成以下方式:
1.如果要把id为1000的数据移动到第2位。目标位置相邻的两个数据sort_num分别为1024和2048,则将id为1000的数据的sort_num改为1024 + (2048 - 1024) / 2 = 1536。
2.如果再把id为999的数据移动到第2位。将id为999的数据的sort_num改为1024 + (1536 - 2014) / 2 = 1280。
...
经过10次后第1位和第2位数之间的sort_num变为1024和1025,两个数据之间的间隔只相差1,此时不能再插入数据了。此时需要做一次全局调整,将所有的sort_num恢复到初始值。这样每一个位置上最大可以不断移动10次,这就大大减少了批量移动的次数。
恢复初始值SQL示例:
update table_name t, (select @rownum:=@rownum+1 rownum,table_name.* from table_name, (select @rownum := 0) tmp order by sort_num) i set t.sort_num =i.rownum where t.id = i.id;
在整个过程中需要做好边界条件的判断,首尾都要有个挡板数据,首部挡板数据可以设置为0,尾部可以设置为(count(*) + 1) *1024。
以上方案在数据量比较大的情况下不太适用,不过通常情况下有这种需求的数据通常不会太大。