使用 PostgreSQL 更新前 N 个值
Posted
技术标签:
【中文标题】使用 PostgreSQL 更新前 N 个值【英文标题】:Update top N values using PostgreSQL 【发布时间】:2012-10-22 15:37:25 【问题描述】:我想更新表中列的前 10 个值。我有三列; id
、account
和 accountrank
。要获得前 10 个值,我可以使用以下方法:
SELECT * FROM accountrecords
ORDER BY account DESC
LIMIT 10;
我想做的是根据account
的大小将accountrank
中的值设置为一系列1 - 10
。这可以在 PostgreSQL 中实现吗?
【问题讨论】:
如果您的 poatgres 版本是 8.4 或更高版本,您可以使用窗口函数 + rank() 或 row_number()。 【参考方案1】:WITH cte AS (
SELECT id, row_number() OVER (ORDER BY account DESC NULLS LAST) AS rn
FROM accountrecords
ORDER BY account DESC NULLS LAST
LIMIT 10
)
UPDATE accountrecords a
SET accountrank = cte.rn
FROM cte
WHERE cte.id = a.id;
加入表表达式通常比关联子查询快。它也更短。
window function row_number()
保证不同的数字。如果您希望 account
具有相同值的行共享相同的编号,请使用 rank()
(或可能是 dense_rank()
)。
只有在account
中可以有NULL
值时,才需要附加NULLS LAST
进行降序排序,或者NULL
值在顶部排序:
如果可以同时进行写访问,则上述查询受制于竞态条件。考虑:
Atomic UPDATE .. SELECT in Postgres Postgres UPDATE … LIMIT 1但是,如果是这样的话,硬编码前十名的整个概念将是一种可疑的方法。
使用 CTE 而不是普通的子查询(就像我一开始那样)来可靠地强制执行 LIMIT
。请参阅上面的链接。
【讨论】:
【参考方案2】:当然,您可以在子查询中使用您的 select 语句。生成排名顺序并非易事,但这里至少有一种方法可以做到这一点。我还没有测试过这个,但在我的脑海中:
update accountrecords
set accountrank =
(select count(*) + 1 from accountrecords r where r.account > account)
where id in (select id from accountrecords order by account desc limit 10);
这有一个怪癖,如果两条记录具有相同的account
值,那么它们将获得相同的排名。您可以认为这是一个功能... :-)
【讨论】:
以上是关于使用 PostgreSQL 更新前 N 个值的主要内容,如果未能解决你的问题,请参考以下文章