在关系数据库中表示顺序
我在数据库中有一组对象。照片库中的图像,目录中的产品,书中的章节等。每个对象表示为一行。我希望能够任意排序这些图像,并将该排序存储在数据库中,这样当我显示对象时,它们将以正确的顺序排列。
例如,假设我正在写一本书,而每一章都是一个对象。我写书,并按以下顺序排列各章:
Introduction, Accessibility, Form vs. Function, Errors, Consistency, Conclusion, Index
它进入编辑器,并按照以下建议顺序返回:
Introduction, Form, Function, Accessibility, Consistency, Errors, Conclusion, Index
如何以健壮,有效的方式将这种排序存储在数据库中?
我有以下想法,但我对其中任何一个都不感到兴奋:
- 大批。每行都有一个订购ID,当更改订单时(通过删除然后插入),订单ID会更新。因为它只是
ORDER BY
,所以检索起来很容易,但似乎很容易中断。
// REMOVAL UPDATE ... SET orderingID=NULL WHERE orderingID=removedID UPDATE ... SET orderingID=orderingID-1 WHERE orderingID > removedID // INSERTION UPDATE ... SET orderingID=orderingID+1 WHERE orderingID > insertionID UPDATE ... SET orderID=insertionID WHERE ID=addedID
- 链表。每行都有一列,表示排序中下一行的ID。在这里遍历似乎代价很高,尽管可能会以某种方式使用我没有想到的" ORDER BY"。
- 间隔的数组。设置orderingID(在#1中使用)较大,因此第一个对象是100,第二个对象是200,依此类推。然后,当发生插入时,只需将其放置在"(objectBefore + objectAfter)/ 2"上。当然,这有时需要重新平衡,因此我们之间的关系不会太紧密(即使使用浮点数,我们最终也会遇到舍入错误)。
这些对我来说似乎都不是那么优雅。有人有更好的方法吗?
解决方案
回答
Rails中的acts_as_list mixin基本上按照我们在#1中概述的方式处理此问题。它会寻找一个名为position的INTEGER列(我们可以将其当然覆盖为名称),然后使用该列执行ORDER BY。当我们想重新排序商品时,我们可以更新职位。每次使用它,对我来说都很好。
附带说明一下,我们可以通过使用稀疏编号来消除始终在INSERTS / DELETES上重新定位的需要-有点像以前的基础...我们可以编号10、20、30等职位,如果我们需要在10到20之间插入内容,则只需将其插入位置为15. 同样,在删除时,我们也可以删除行并保留空白。我们只需要在实际更改顺序或者尝试插入时没有重新编号的情况下重新编号。
当然,根据特定情况(例如,是否已将其他行加载到内存中),使用间隙方法可能有意义,也可能没有意义。
回答
我会做一个连续的数字,在表上有一个触发器,该触发器为优先级"腾出空间"(如果已经存在)。
回答
如果对象没有被其他表大量使用键,并且列表很短,则删除域中的所有内容并重新插入正确的列表是最容易的。但是,如果列表很大并且我们有很多限制来减慢删除速度,那是不切实际的。我认为第一种方法确实是最干净的。如果我们在事务中运行它,则可以确保在更新过程中弄乱订单的过程中不会发生任何奇怪的情况。
回答
我在上一个项目中做到了这一点,但这是为了只偶尔需要对表进行排序,并且访问频率不是很高。我认为间隔数组将是最好的选择,因为在一般情况下,重新排序将是最便宜的,只需更改一个值并查询两个值。
而且,我想数据库供应商将对ORDER BY进行相当大的优化,因此与链接列表实现相比,利用该功能将对性能有利。
回答
另一种选择是(如果RDBMS支持)使用数组类型的列。尽管这违反了规范化规则,但在此类情况下可能很有用。我知道的一个具有数组的数据库是PostgreSQL。
回答
只是考虑选项#1与#3的想法:间隔数组选项(#3)不会仅推迟正常数组(#1)的问题吗?无论选择哪种算法,它都会被破坏,然后我们将在#3中遇到问题,或者它会起作用,然后#1应该也能起作用。
回答
使用浮点数表示每个项目的位置:
项目1-> 0.0
项目2-> 1.0
项目3-> 2.0
项目4-> 3.0
我们可以通过简单的二等分将任何项目放置在任何其他两个项目之间:
项目1-> 0.0
项目4-> 0.5
项目2-> 1.0
项目3-> 2.0
(将项目4移至项目1和2之间)。
由于计算机系统中浮点数的编码方式,对分过程几乎可以无限期地继续进行。
项目4-> 0.5
项目1-> 0.75
项目2-> 1.0
项目3-> 2.0
(将项目1移动到项目4之后的位置)
回答
我也有这个问题。我承受着沉重的时间压力(不是所有人),我选择了选项1,并且只更改了更新的行。
如果将项目1与项目10交换,只需进行两次更新以更新项目1和项目10的订单号。我知道这在算法上很简单,这是O(n)最坏的情况,但是最糟糕的情况是列表的总排列。这种情况多久发生一次?那是你的答案。
回答
由于我在Django中经常遇到这种情况,因此我发现此解决方案是最可行的。似乎在关系数据库中没有任何"正确的方法"可以做到这一点。