任意排序表中的记录

Tom*_*nal 32 database-design order-by database-agnostic relational-theory

使用数据库时的一个常见需求是按顺序访问记录。例如,如果我有一个博客,我希望能够以任意顺序重新排列我的博客文章。这些条目通常有很多关系,因此关系数据库似乎很有意义。

我见过的常见解决方案是添加一个整数列order

CREATE TABLE AS your_table (id, title, sort_order)
AS VALUES
  (0, 'Lorem ipsum',   3),
  (1, 'Dolor sit',     2),
  (2, 'Amet, consect', 0),
  (3, 'Elit fusce',    1);
Run Code Online (Sandbox Code Playgroud)

然后,我们可以对行进行排序order以使它们按正确的顺序排列。

然而,这似乎很笨拙:

  • 如果我想将记录 0 移到开头,我必须对每条记录重新排序
  • 如果我想在中间插入一条新记录,我必须在它之后重新排列每条记录
  • 如果我想删除一条记录,我必须在它之后对每条记录重新排序

很容易想象这样的情况:

  • 两条记录相同 order
  • order记录之间有间隙

由于多种原因,这些可能很容易发生。

这是 Joomla 等应用程序采用的方法:

Joomla 的订购方法示例

你可能会争辩说这里的界面很糟糕,他们应该使用箭头或拖放而不是人类直接编辑数字——你可能是对的。但在幕后,同样的事情正在发生。

有些人建议使用小数来存储订单,这样你就可以使用“2.5”在订单 2 和 3 的记录之间插入一条记录。虽然这有点帮助,但可以说它更混乱,因为你最终可能会得到奇怪的小数(你在哪里停?2.75?2.875?2.8125?)

有没有更好的方法将订单存储在表中?

Mik*_*ll' 18

如果我想将记录 0 移到开头,我必须对每条记录重新排序

不,有一个更简单的方法。

update your_table
set order = -1 
where id = 0;
Run Code Online (Sandbox Code Playgroud)

如果我想在中间插入一条新记录,我必须在它之后重新排列每条记录

这是真的,除非您使用支持“介于”值的数据类型。浮点型和数字类型允许您将值更新为 2.5。但是 varchar(n) 也有效。(想想“a”、“b”、“c”;然后想想“ba”、“bb”、“bc”。)

如果我想删除一条记录,我必须在它之后对每条记录重新排序

不,有一个更简单的方法。只需删除该行。其余行仍将正确排序。

很容易想象这样的情况:

两条记录具有相同的顺序

唯一约束可以防止这种情况发生。

记录之间的顺序存在差距

间隙对 dbms 如何对列中的值进行排序没有影响。

有些人建议使用小数来存储订单,这样你就可以使用“2.5”在订单 2 和 3 的记录之间插入一条记录。虽然这有点帮助,但可以说它更混乱,因为你最终可能会得到奇怪的小数(你在哪里停?2.75?2.875?2.8125?)

你不停止,直到你来。dbms对小数点后有 2、7 或 15 位的值进行排序没有问题。

我认为你真正的问题是,你想看到的排序顺序值作为整数。你可以这样做。

create table your_table (
  id int primary key, 
  title varchar(13), 
  sort_order float
);
    
insert into your_table values
(0, 'Lorem ipsum', 2.0),
(1, 'Dolor sit', 1.5),
(2, 'Amet, consect', 0.0),
(3, 'Elit fusce', 1.0);

-- This windowing function will "transform" the floats into sorted integers.
select id, title,
       row_number() over (order by sort_order)
from your_table
Run Code Online (Sandbox Code Playgroud)


小智 9

这很简单。你需要有一个“基数洞”结构:

您需要有 2 列:

  1. pk = 32 位 integer
  2. 订单 = 64 位bigint不是 double

插入/更新

  1. 插入第一条新记录时,设置order = round(max_bigint / 2).
  2. 在表的开头插入时,设置 order = round("order of first record" / 2)
  3. 在表尾插入时,设置order = round("max_bigint - order of last record" / 2) 4) 在中间插入时,设置order = round("order of record before - order of record after" / 2)

这个方法有一个非常大的基数。如果您有约束错误,或者您认为基数很小,则可以重建订单列(标准化)。

在标准化(使用这种结构)的最大情况下,您可以在 32 位中拥有“基数洞”。

记住不要使用浮点类型 - order 必须是一个精确的值!


小智 5

一般来说,排序是根据记录中的一些信息、标题、ID 或任何适合特定情况的信息来完成的。

如果您确实需要特殊的排序,那么使用整数列并不像看起来那么糟糕。例如,要为第五名的记录腾出空间,您可以执行以下操作:

update table_1 set place = place + 1 where place > 5

希望您可以声明该列是unique并且也许有一个程序可以使重新排列“原子”。细节取决于系统,但这是总体思路。