某些应用程序,例如待办事项列表,需要维护用户定义的项目顺序。挑战在于顺序是任意的,并且可以在用户重新排列项目时改变
一个示例表可能是这样的:
create table items
(
itemid int primary key,
itemdata varchar(200),
...
...
userorder ???
);
Run Code Online (Sandbox Code Playgroud)
使用可能的检索查询:
select * from items order by userorder asc;
Run Code Online (Sandbox Code Playgroud)
考虑到我希望将更新保持在最低限度、更新原子性和检索查询尽可能简单和“高效”,我是否应该考虑任何方法?
我最初的想法是使用一个额外的TIMESTAMP
列,并且只更新单行userorder
的timestamp
值(使用ORDER
ing by检索查询userorder asc, usertimestamp desc
),但是当您需要在具有相同值的其他两行之间移动一行时失败.
没有考虑特定的数据库,因为我自己使用的范围很广。
考虑到我希望将更新保持在最低限度、更新原子性和检索查询尽可能简单和“高效”,我是否应该考虑任何方法?
我认为这是该博客文章中“真实分数”方法的深奥变体——优点是不需要引入用户定义的类型(至少如果您使用 Postgres)。
这可以使用自然排序属性varbit
并将它们的序列映射到二叉树上,以便始终可以varbit
在任何两个现有相邻值之间生成另一个值:
_____ 1 ____
______/ \______
_ 01 _ 11
_/ \_ _/ \_
001 011 101 111
/ \ / \ / \ / \
0001 0011 0101 0111 1001 1011 1101 1111
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)create table foo(id serial primary key, userorder varbit unique); create function adj(orig varbit, b varbit) returns varbit language sql as $$ select substring(orig for length(orig)-1) ||b ||substring(orig from length(orig)); $$; create function f(l varbit, h varbit) returns varbit language plpgsql as $$ begin if l is null and h is null then return B'1'; end if; if l is null then return adj(h,B'0'); end if; if h is null then return adj(l,B'1'); end if; if length(l)>length(h) then return adj(l,B'1'); end if; return adj(h,B'0'); end; $$;
Run Code Online (Sandbox Code Playgroud)insert into foo(userorder) values(f(null,null)); select * from foo order by userorder;
身份证 | 用户订单 -: | :-------- 1 | 1
Run Code Online (Sandbox Code Playgroud)insert into foo(userorder) values(f(null,B'1')); select * from foo order by userorder;
身份证 | 用户订单 -: | :-------- 2 | 01 1 | 1
Run Code Online (Sandbox Code Playgroud)insert into foo(userorder) values(f(B'01',B'1')); select * from foo order by userorder;
身份证 | 用户订单 -: | :-------- 2 | 01 3 | 011 1 | 1
Run Code Online (Sandbox Code Playgroud)insert into foo(userorder) values(f(B'01',B'011')),(f(B'011',B'1')),(f(B'1',null)); select * from foo order by userorder;
身份证 | 用户订单 -: | :-------- 2 | 01 4 | 0101 3 | 011 5 | 0111 1 | 1 6 | 11
dbfiddle在这里
如果您最初加载大量有序行,您可能需要使用其他一些算法来生成初始userorder
值,因为您将遇到空间使用的最坏情况(每行将比userorder
之前的行多使用一位)。对于足够多的位(例如,对于 8 个值:B'0001'
, B'0011'
, B'0101'
, B'0111'
, B'1001'
, B'1011'
, B'1101'
, B'1111'
),您可以逐步遍历相同长度的值。
一些非常古老的 BASIC 语言要求每行代码都有一个行号。
编程时,行号通常以 10 的倍数间隔。这允许您以后在行之间添加更多代码。
您的items
表格应该遵循相同的概念。
userorder
是 10 的倍数,每个值从 10 开始userid
userorder
根据需要更新应用程序userorder
您使用 Analytics重新编号该用户commit
数据发生变化。这基本上是我用过的:
merge into items a
using (
select i.itemid
,10 * row_number() over (partition by i.userid order by i.userorder)
as new_userorder
from items i
where i.userid=?
) b
on (a.itemid = b.itemid)
when matched then update
set a.userorder=b.new_userorder
;
Run Code Online (Sandbox Code Playgroud)
我假设这个表保存了每个人的 TODO 列表,并且每个用户都由 标识userid
。
本示例使用ROW_NUMBER()
来自 Oracle 的。您必须用它来替换您正在使用的任何 RDBMS。
归档时间: |
|
查看次数: |
1478 次 |
最近记录: |