【问题标题】:What's the best way to store sort order in SQL?在 SQL 中存储排序顺序的最佳方式是什么?
【发布时间】:2019-02-28 17:24:09
【问题描述】:

顶部的人希望在我们的应用程序中可以自定义排序顺序。所以我有一个有效定义数据类型的表。存储排序顺序的最佳方式是什么。如果我刚刚创建了一个名为“Order”之类的新列,那么每次我更新一行的顺序时,我想我都必须更新每一行的顺序以确保后代。有更好的方法吗?

    标签: sql database sorting


    【解决方案1】:

    到目前为止,没有一个答案涉及自定义排序顺序的真正问题,当两个不同的人希望相同的记录以不同的方式排序时,就会发生这种情况。

    如果您需要自定义排序顺序,则需要一个相关表来存储它,而不是额外的字段。该表将具有用户 ID、数据的记录 ID 和记录的排序顺序。这样,对于相同的数据,Joe Smith 可以有一个订单,而 Sally Jones 可以有另一个。现在您遇到了将新记录添加到数据集中的问题。您是将它们放在排序顺序的开头还是结尾,或者您是否需要该人为它们设置顺序,然后才能将它们添加到集合中。这实际上是一个非常复杂的问题,通常不值得花费大量时间来实施,因为一旦该系统到位,几乎没有人使用过(我的意思是我真的想检查一百条记录并标记个人每个的顺序?)。现在它在保存所有记录的顺序方面变得复杂了(这当然需要在下次运行查询时进行更改,因为会有新记录。)这是一个非常痛苦的有限直到过程。

    我在一个提案编写应用程序中这样做过一次,因为我们需要能够按照我们认为对客户印象最深刻的顺序对提案中的部分和任务进行排序。即便如此,我们也必须制定一个默认订单,这样他们只需要移动他们真正想首先展示的两三件东西,而不是订购 10,000 个单独的零件。

    如果您可以让他们购买它,一个更好的选择是允许他们按列(desc 或 asc)对数据进行排序。通常可以设计用户界面,以便如果您单击列标题,它将按该列处理数据。这相对简单,可以满足大多数定制订购的需求。

    您确实需要与管理层讨论此要求,并了解他们希望它如何工作的详细信息,我想要定制订购。这通常是人们认为他们想要但并没有真正使用的东西之一。

    【讨论】:

    • +1 用于从最终用户处获取规范,但此外还用于解释中的详细信息
    • 好答案。我会与他们核实需求,但我相信最终会是完全可定制的订单,但可能是一个全球订单。该订单只能由管理员编辑,并可供所有用户使用。
    • “(我的意思是我真的要遍历一百条记录并标记每条记录的顺序吗?)” 嗯……是吗?谁说您不想在插入记录时对其进行排序?实际上,这是使用 SQL DB 的明显缺点。
    【解决方案2】:

    基本算法可能如下所述。最初,排序字段因项目而异,相差 1000(您可以考虑另一个间隔)。 为简单起见,表中的项目处于有序状态。 顺便说一句,我创建了Yii2 component 来管理这些东西。如果你需要一个可排序的树sortable tree,这个。

    id | sort
    ---+-----
    1  | 1000
    ---+-----
    2  | 2000
    ---+-----
    3  | 3000
    ---+-----
    

    假设我们要在 id 1 之后添加一个项目(id 4):

    id | sort
    ---+-----
    1  | 1000
    ---+-----
    4  | 1500
    ---+-----
    2  | 2000
    ---+-----
    3  | 3000
    ---+-----
    

    因此,为了计算 id 4 的排序值,我们取了之前项目的排序值,即 1000 和之后的项目 - 2000 并取平均值。如果你得到一个浮点数,只需将它四舍五入到最接近的整数。 如果您需要在列表的开头插入一个项目,那么您取(1000 和 0,即 500)的平均值。

    现在,如果我们需要在 id 1 之后插入一个项目(id 5),我们也这样做:

    id | sort
    ---+-----
    1  | 1000
    ---+-----
    5  | 1250
    ---+-----
    4  | 1500
    ---+-----
    2  | 2000
    ---+-----
    3  | 3000
    ---+-----
    

    稍后,您可能会遇到这种情况:

    id | sort
    ---+-----
    1  | 1000
    ---+-----
    15 | 1001
    ---+-----
    ...
    ---+-----
    5  | 1250
    ---+-----
    ...
    ---+-----
    

    因此,如果您需要在 1 到 15 之间插入一个项目(id 16),首先您应该将所有项目的排序字段增加 1000,然后再增加 1:

    id | sort
    ---+-----
    1  | 1000
    ---+-----
    15 | 2001
    ---+-----
    ...
    ---+-----
    5  | 2250
    ---+-----
    ...
    ---+-----
    

    现在您可以插入项目(id 16):

    id | sort
    ---+-----
    1  | 1000
    ---+-----
    16 | 1501
    ---+-----
    15 | 2001
    ---+-----
    ...
    ---+-----
    5  | 2250
    ---+-----
    ...
    ---+-----
    

    【讨论】:

    • 是的,在您到达必须为表中的每个后部项目增加一个字段以插入 1 的部分之前,这非常有效。
    • @Andrew 正是因为这个问题,你有 1000 的间隔(或任何你选择的间隔)不要在每次写入时更改所有项目。
    • 但这不是一个永久的解决方案。最终你会遇到我所说的发生的情况,然后你无法保持一致的快速速度。更糟糕的是,一旦发生,它可能会多次发生。
    • @Andrew,如果您查看答案中提到的 Yii2 组件-一旦出现这种情况,所有排序字段都将被重新计算和更新。而且它一定不会对性能产生太大影响,因为它是一种罕见的操作,特别是如果间隔足够大,您可以根据表的更新频率来选择。
    • 您将更新整个表中的每个字段。这是不可接受的。
    【解决方案3】:

    您可以改用浮点数,只要您有足够的精度,您始终可以将移动记录的序数列设置为两侧记录之间的中点。

    【讨论】:

    • 您是否需要定期重新调整这些?我想,经过几十万次操作,依靠 fp 的精度可能会变得粗略。
    【解决方案4】:

    使用 int 字段。当您更新一行的排序顺序时,您只需更新您正在更新的行上的字段以及该行旧位置和新位置之间的任何行。这意味着交换两行只涉及接触那两行。此外,对于您要更新的不是“活动”行的行,您只需要增加或减少该字段;查询很容易编写。

    【讨论】:

    • 当在中间删除行并添加新行时会发生什么
    • 或者当您用完中间数字时-谢尔盖的回答中存在同样的问题
    • 存储 int,但按字符串排序。当相邻行之间的插槽用完并想要插入时,请在下限的末尾粘贴一个“5”。因此,如果您的前三个插入是“a”、“c”、“b”,那么您的排序顺序列条目可能是“1”、“2”、“15”。这允许任何一种情况都没有太多的体操。
    • 然后你最终会有很长的字符串。不是永久的解决方案。类似于谢尔盖的回答。
    • 是的,但问题是这对您的数据集是否重要。除非插入顺序是病态的,否则平均字符串长度将为 O(log(n)),字符串比较速度取决于两者中较短者的长度。此外,除非您有理由将存储的字符串设为十进制数字,否则您可以使用 base64 字符集并将其设为 O(log64(n)),这是进一步压缩它的一种非常好的方法。如果您的数据库允许对二进制文件进行字符串比较,则可以将其设置为 O(log256(n))。
    【解决方案5】:

    通常,应用程序会将适当的 ORDER BY 子句添加到查询中。如果要排序的结果集相对较小,您可以在选择标准上设置键。即使结果很大,对选定的数据进行排序通常也比按索引按顺序检索要好。

    如果要求有像 B A Z T Q M K 这样的订单,那么您将需要一个列来放置相关订单。每次添加行时都需要确定适当的值。但是,这对于相对静态的代码表很有效。

    【讨论】:

      猜你喜欢
      • 2011-02-19
      • 2012-07-15
      • 2016-09-13
      • 2011-04-17
      • 1970-01-01
      • 2014-07-13
      • 1970-01-01
      • 2012-07-26
      • 2016-09-29
      相关资源
      最近更新 更多