在PostgreSQL中维护有序列表的最佳方法是什么?

时间:2017-12-12 03:16:55

标签: postgresql

假设我有一个名为list的表格,其中item就像这些(id是随机的uuids):

id  rank  text
--- ----- -----
x   0     Hello
x   1     World
x   2     Foo
x   3     Bar
x   4     Baz

我想维护rank列始终从0n-1n是行数)的属性---如果客户要求insert项目为rank = 3,然后pg服务器应将当前34分别推送到45

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列完全独立于其余列,并且插入不是我需要支持的唯一操作。可以将其视为可排序待办事项列表的后端,用户可以动态添加/删除/重新排序项目。

3 个答案:

答案 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窗口函数计算的整数。

当插入一行应排在两行之间时,将新等级计算为两个等级的算术平均值。

优点是您不必更新现有行 缺点是你必须先计算显示的排名才能插入新行,以便知道插入的位置。

此解决方案(与所有其他解决方案一样)受竞赛条件限制 要处理这些问题,您可以使用表锁或可序列化的事务。