2020 年了,现在对于拖拽排序有什么更好的存储方案?

2020-05-27 15:11:37 +08:00
 raysonlu

记得以前设计 CMS 系统的时候接触过这个问题,

文章表加一个 sort,然后让用户自己填写 sort 值,越大就越靠前,再加个 id 排序解决同样 sort 值问题,这种很适合翻页排序(特别是翻页后不显示其他页内容),但需要用户自己填写数值感觉不太友好。

后来出现了“拖拽排序”的交互,这种填值方式就走不通了,置顶和置尾还可以前端自动填值,中间的移动就没办法了,除非移动后把后面所有文章的 sort 值都 update 一次,少量文章还可以,量大了而且还可能涉及到多人同时操作的话,要不得。

寻求过其他方法,很多都说用“除二法”, 就是每个文章排序号中间有间距( 100 和 200),每往中间拖一个,sort 值就改为前后间距的二分一(150),如此类推,一直除下去将会是浮点数。 且不说浮点数精准度会不会出现问题,乍看这个方案貌似有“次数限制”,如果不停地往“某个中间”拖入一个,浮点数的长度就是限制?后来想到一个法子规避这个次数限制,用定时任务方法定期优化一下排序数字,这算是个可行但很难的方案吧~~~

现在又遇到一个类似的开发需求,想咨询一下 2020 年了有无更好的解决方案?

5809 次点击
所在节点    程序员
36 条回复
sarvatathagata
2020-05-27 19:15:21 +08:00
作为一个 OIer,一般用替罪羊树做动态标号就行了。
Akkuman
2020-05-27 19:28:21 +08:00
100/(2^1000)=9.3326362e-300

DOUBLE——一个双精度浮点数。支持 -1.7976931348623157E+308 到-2.2250738585072014E-308,0 和 2.2250738585072014E- 308 到 1.7976931348623157E+308 。如果是 FLOAT,UNSIGNED 不会改变正数范围,但负数是不允许的。
• DOUBLE PRECISION——同 DOUBLE
• REAL——同 DOUBLE
• DECIMAL——将一个数像字符串那样存储,每个字符占一个字节
• DEC——同 DECIMAL
• NUMERIC——同 DECIMAL

也就是说,你往同一个地方不停地得拖动一千多次,才会出现次数限制,现实场景好像并不多,如果担心可以用 decimal
index90
2020-05-27 19:32:24 +08:00
???
是我理解错题目了?
难道不是在一个 10W 个元素的数组中,把第 10 个元素移动到第 5 个元素的位置吗?那么只要重排 5~10 位置上的元素就可以啦。跟第 11,12,13,第 10 万的元素有什么关系呢?时间复杂度就是元素移动的距离。
好像还有置顶和置尾功能?用标记就好啦,查询的时候用 where 过滤一下。

越看评论越觉得我弱智了,是我理解错了吗?
agagega
2020-05-27 19:38:19 +08:00
别用浮点数,用字符串。A 和 B 中间的,就是 AA
Shy07
2020-05-27 21:03:30 +08:00
@raysonlu
我说的文章 id 单独保存一个排序数组是指:id 1~5 的文章就单独保存一个 [1...5] 的数组 a

分页显示 [1, 2, 3] ,排序后是 [3, 2, 1] 的话,就更新数组 a page 和 pageSize 范围内的值。

这样的好处是不用修改文章数据,你在排序的时候,别人也可以编辑文章内容;同时文章本身是如何存储的、顺序如何数组 a 完全不 care.
px920906
2020-05-27 21:45:55 +08:00
之前正好也搜过相关的方案,teambition 就是“除二法”,每次请求后都检查一遍,不满足相邻最小差值就重新计算并返回新的值给前端 -> https://www.zhihu.com/question/55789722/answer/146650607
(回答好像写错了吧
raysonlu
2020-05-28 09:27:41 +08:00
@index90 不好意思,我那个举例的确有一些问题,如你所说“时间复杂度就是元素移动的距离”是正确的,但我初衷也是想解决这个问题。
也的确要“支持跨页拖拽”,按我的理解,出现了“拖拽”这个交互后,仅仅显示一页的内容让用户拖拽排序是没意义的吧(比如把下一页的第一个拖拽到这一页中间),这时候一般做下拉式分页展示,也因此,跨度距离就变得不可控(想象中的交互场景:用户抓起某个文章,移动到靠上(下)的位置,页面不停往上(下)滚动,然后找到位置放下)
mazhan465
2020-05-28 10:20:39 +08:00
为啥不用链表呢,或者跳表?
raysonlu
2020-05-28 10:30:02 +08:00
@mazhan465 链表方案也有看过,但这种不好在数据库里查找吧?
mazhan465
2020-05-28 11:07:56 +08:00
@raysonlu 表加个 next 字段,保存下个节点的 id,比如当前顺序 1,2,3,4,5,将 5 拖到 2,3 之间,查 next=3 的改为 next=5,next=5 的改为 5 的 next,也就是 NULL 或者 0
mazhan465
2020-05-28 11:08:22 +08:00
@mazhan465 这样做的缺点就是不把数据全倒出来无完成排序
raysonlu
2020-05-28 11:21:16 +08:00
@mazhan465 #13 是啊,分页排序本是类似“把数据全倒出来”的活儿,只是数据库的 order 帮我们优美地干了,但这种链表的方案,就只能做在业务代码层了,这就”真·全倒出来“了~
no1xsyzy
2020-05-28 13:33:48 +08:00
@Akkuman #22 你必须往 0 附近拖动导致指数不断改变才能这么算,不然双精度最多 51 次二分。
no1xsyzy
2020-05-28 13:44:39 +08:00
不太确定还需要何种访问方式,盲狙一个 B-tree
raysonlu
2020-05-28 17:20:54 +08:00
@no1xsyzy 请教如何实现
no1xsyzy
2020-05-28 18:34:27 +08:00
@raysonlu #35 做一张表来实现,表的一行代表 B-tree 的一个节点,用 id 来引用
其实各种树都可以这么玩,只不过访问数据库次数比较关键,选访问次数比较低的树吧,宽度比较大的 B-tree 会比较好。
只不过 “给定项目,获知其在第几个位置” 的查询比较麻烦

更加元分析地:一维数组,处理序相关问题基本就是树和桶,访问次数低的就是 B-tree 和 “二除法”。
盲狙一个 B-tree 没错的,不过也就是盲狙,并没有相应场景的实际的演练。
桶其实大部分情况下可以用了,遇到极限情况进行重建就行。就算是定时任务,做好注释问题不大。
如果不能满足要求那就是树了。

也可能不是你自己实现,采用数据库内已经是树方式实现的索引方式也是可以的,访问次数也很低。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/675986

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX