假设我有一个名为list
的表格,其中item
就像这些(id
是随机的uuids):
id rank text
--- ----- -----
x 0 Hello
x 1 World
x 2 Foo
x 3 Bar
x 4 Baz
我想维护rank
列始终从0
到n-1
(n
是行数)的属性---如果客户要求insert
项目为rank = 3
,然后pg服务器应将当前3
和4
分别推送到4
和5
:
id rank text
--- ----- -----
x 0 Hello
x 1 World
x 2 Foo
x 3 New Item!
x 4 Bar
x 5 Baz
我目前的策略是使用专用的插入函数add_item(item)
来扫描表格,过滤出等于或大于所插入项目等级的项目,并将这些等级增加一。但是,我认为这种方法会遇到各种各样的问题 - 比如竞争条件。
是否有更标准的做法或更强大的方法?
注意:rank列完全独立于其余列,并且插入不是我需要支持的唯一操作。可以将其视为可排序待办事项列表的后端,用户可以动态添加/删除/重新排序项目。
答案 0 :(得分:0)
按照你的建议逐字逐句可能很难或根本不可能,但我可以建议一个解决方法。维护一个新列ts
,用于存储插入记录的时间。然后,插入当前时间以及记录的其余部分,即
id rank text ts
--- ----- ----- --------------------
x 0 Hello 2017-12-01 12:34:23
x 1 World 2017-12-03 04:20:01
x 2 Foo ...
x 3 New Item! 2017-12-12 11:26:32
x 3 Bar 2017-12-10 14:05:43
x 4 Baz ...
现在,我们可以通过查询轻松生成您想要的订单:
SELECT id, rank, text,
ROW_NUMBER() OVER (ORDER BY rank, ts DESC) new_rank
FROM yourTable;
这将在上面的示例表中生成0到5个等级。基本的想法是只使用现有的rank
列,但是如果相同的排名出现多次,那么让时间戳打破排序的关系。
答案 1 :(得分:0)
如果你认为它值得:
,你可以把它包起来t=# with u as (
update r set rank = rank + 1 where rank >= 3
)
insert into r values('x',3,'New val!')
;
INSERT 0 1
结果:
t=# select * from r;
id | rank | text
----+------+----------
x | 0 | Hello
x | 1 | World
x | 2 | Foo
x | 3 | New val!
x | 4 | Bar
x | 5 | Baz
(6 rows)
还值得一提的是,你可能有并发性和追逐状态"高负荷系统的问题。上面的代码只是一个示例
答案 2 :(得分:0)
您可以拥有一个“计算排名”,即double precision
和一个“显示排名”,这是一个使用输出上的row_number
窗口函数计算的整数。
当插入一行应排在两行之间时,将新等级计算为两个等级的算术平均值。
优点是您不必更新现有行 缺点是你必须先计算显示的排名才能插入新行,以便知道插入的位置。
此解决方案(与所有其他解决方案一样)受竞赛条件限制 要处理这些问题,您可以使用表锁或可序列化的事务。