常见的解决方案是添加一个整数列
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);
然后,我们可以按
order
对行进行排序,以使其按正确的顺序排列。但是,这看起来很笨拙:
如果要将记录0移到开头,则必须对每个记录重新排序
如果要在中间插入新记录,则必须对每个记录重新排序
如果要删除一条记录,我必须重新排序每条记录
很容易想到这样的情况:
两个记录具有相同的
order
< br记录之间在
order
中存在间隙由于多种原因,这些间隙很容易发生。
Joomla等应用程序采用的方法是:
/>
您可能会争辩说这里的界面不好,不是人类直接编辑数字,则应使用箭头或拖放操作-您可能是对的。但是在幕后,发生了同样的事情。
有人建议使用小数存储顺序,以便您可以使用“ 2.5”将记录插入顺序2和顺序之间的记录中。 3.虽然有一点帮助,但可以说它甚至更混乱,因为您可能会得到奇怪的小数点(在哪里停止?2.75?2.875?2.8125?)
是否有更好的存储订单的方法在桌子上?
#1 楼
如果要将记录0移到开头,则必须对每个记录重新排序
不,有一种更简单的方法。
update your_table
set order = -1
where id = 0;
如果我想在中间插入一条新记录,我必须对它之后的每条记录重新排序。
是的,除非您使用支持“介于”值之间的数据类型。浮点数和数字类型使您可以将值更新为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
评论
为了简洁起见,您可以使用cte之类的方法完成此工作(在测试中,选择*,row_number()超过(按sort_order desc排序)作为测试行)
– Manngo
17年3月28日在11:23
这是一个附加提示:如果您希望它真的很完美,则应检查是否要移动更多行,然后希望保持不变。如果是这样,则更新数量较少的“未触动”的那些; D
– Ruben Boeck
18年5月12日11:00
#2 楼
这很简单。您需要具有“基数孔”结构:您需要具有2列:
pk = 32位
integer
order = 64位
bigint
(不是double
)插入/更新
插入第一个新记录时,设置
order = round(max_bigint / 2)
。在开始插入时在表的末尾,设置
order = round("order of first record" / 2)
在表尾插入时,设置
order = round("max_bigint - order of last record" / 2)
4)在中间插入时,设置
order = round("order of record before - order of record after" / 2)
此方法具有很大的基数。如果遇到约束错误,或者如果您认为基数很小,则可以重建订单列(规范化)。
在具有规范化的最大情况下(采用这种结构),您可以在32中设置“基数漏洞”位。
请记住不要使用浮点类型-顺序必须是精确值!
#3 楼
通常,根据记录,标题,ID或适用于该特定情况的任何信息来进行排序。如果您确实需要特殊的排序,那么使用整数列并不像看起来那样糟糕。例如,要为记录排在第5位留出空间,您可以执行以下操作:
update table_1 set place = place + 1 where place > 5
。希望您可以将该列声明为
unique
,也许具有使“原子”重排的程序。具体细节取决于系统,但这是基本概念。#4 楼
…甚至可以说是更加混乱,因为您最终可能会得到奇怪的小数点(在哪里停止?2.75?2.875?2.8125?)
谁在乎?这些数字仅由计算机处理,因此它们有多少小数位或我们看上去多么难看都无关紧要。
使用十进制值意味着要在项目J和K之间移动项目F,您需要做的就是选择J和K的顺序值,然后取它们的平均值,然后更新F。两个SELECT语句和一个UPDATE语句(可能使用可序列化隔离来完成以避免死锁)。
如果要在输出中查看整数而不是分数,则可以在客户端应用程序中计算整数或使用ROW_NUMBER()或RANK()函数(如果您的RDBMS包含它们)。
#5 楼
在我自己的项目中,我打算尝试一个类似于十进制数的解决方案,但改用字节数组:def pad(x, x_len, length):
if x_len >= length:
return x
else:
for _ in range(length - x_len):
x += b"\x00"
return x
def order_index(_from, _to, count, length=None):
assert _from != _to
assert _from < _to
if not length:
from_len = len(_from)
to_len = len(_to)
length = max(from_len, to_len)
_from = pad(_from, from_len, length)
_to = pad(_to, to_len, length)
from_int = int.from_bytes(_from, "big")
to_int = int.from_bytes(_to, "big")
inc = (to_int - from_int)//(count + 1)
if not inc:
length += 1
_from += b"\x00"
_to += b"\x00"
return order_index(_from, _to, count, length)
return (int.to_bytes(from_int + ((x+1)*inc), length, "big") for x in range(count))
>>> index = order_index(b"A", b"Z", 24)
>>> [x for x in index]
[b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y']
>>>
>>> index = order_index(b"A", b"Z", 25)
>>> [x for x in index]
[b'A\xf6', b'B\xec', b'C\xe2', b'D\xd8', b'E\xce', b'F\xc4', b'G\xba', b'H\xb0', b'I\xa6', b'J\x9c', b'K\x92', b'L\x88', b'M~', b'Nt', b'Oj', b'P`', b'QV', b'RL', b'SB', b'T8', b'U.', b'V$', b'W\x1a', b'X\x10', b'Y\x06']
这个想法是,您永远不会耗尽中间的值,因为如果需要更多的值,您只需在相关记录中附加一个
b"\x00"
。 (int
在Python 3中是不受限制的,否则,您必须在末尾选择一个字节的切片进行比较,假设是在两个相邻值之间,差异将被压缩到末尾。)例如,假设您有两个记录
b"\x00"
和b"\x01"
,并且您希望在它们之间有一条记录。在0x00
和0x01
之间没有任何可用的值,因此您将b"\x00"
都附加到这两个值之间,现在您可以在它们之间添加一堆值,可以用来插入新值。 该数据库可以轻松地对其进行排序,因为所有内容最终都按字典顺序排列。如果您删除一条记录,它仍然是有序的。但是,在我的项目中,我将
b"\x00"
和b"\xff"
分别作为FIRST
和LAST
记录,以便将它们用作虚拟的“ from”和“ to”值来添加/添加新记录:>>> records = [b"\x00", b"\x01", b"\x02"]
>>> values = [x for x in order_index(records[0], records[1], 3)]
>>> records = records + values
>>> records.sort()
>>> records
[b'\x00', b'\x00@', b'\x00\x80', b'\x00\xc0', b'\x01', b'\x02']
#6 楼
我发现这个答案要好得多。完全引用它:数据库针对某些事物进行了优化。快速更新很多行是其中之一。当您让数据库完成其工作时,这一点尤其成立。
考虑:您将有两个查询:
Beat It
它。这可以很好地按比例放大。尝试将几千首歌曲放入数据库的假设播放列表中,查看将歌曲从一个位置移动到另一个位置需要多长时间。由于它们具有非常标准化的形式:
update table
set order = order - 1
where order >= 2 and order <= 5;
update table
set order = 5
where song = 'Beat It'
您有两个准备好的语句可以重复使用非常有效。
这提供了一些显着的优点-您可以推断表格的顺序。第三首歌曲的
update table
set order = order - 1
where order >= ? and order <= ?;
update table
set order = ?
where song = ?
始终为3。保证这一点的唯一方法是使用连续整数作为顺序。使用伪链接列表,十进制数字或带空格的整数不能保证您具有此属性。在这种情况下,获取第n首歌曲的唯一方法是对整个表进行排序并获取第n条记录。实际上,这比您想象的要容易得多。很容易弄清楚您想做什么,生成两个更新语句,然后让其他人查看这两个更新语句并了解正在执行的操作。
评论
请注意。 。 。 “这种系统之所以被称为“关系式”,是因为术语“关系”基本上只是表的数学术语。 -数据库系统简介,CJ Date,第7版。第25页用于管理订购列表的功能和模式的可能副本
我没听懂的@ MikeSherrill'CatRecall',我已经用旧订单和ddl解决了这个问题。