使用 PostgreSQL 更新前 N 个值

Posted

技术标签:

【中文标题】使用 PostgreSQL 更新前 N 个值【英文标题】:Update top N values using PostgreSQL 【发布时间】:2012-10-22 15:37:25 【问题描述】:

我想更新表中列的前 10 个值。我有三列; idaccountaccountrank。要获得前 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值在顶部排序:

PostgreSQL sort by datetime asc, null first?

如果可以同时进行写访问,则上述查询受制于竞态条件。考虑:

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 个值的主要内容,如果未能解决你的问题,请参考以下文章

按组选择前 N 个值

使用 lambda 查询获取前 5 个值

在 Oracle 中独立地从多个列中有效地查找前 N 个值

在每组中查找前 N 个值

获取列中具有前 2 个值之一的所有行

如何在巨大数据帧的每一行中查找前 n 个值的列索引