Postgres UPDATE 使用排名窗口函数

Posted

技术标签:

【中文标题】Postgres UPDATE 使用排名窗口函数【英文标题】:Postgres UPDATE using rank window function 【发布时间】:2016-10-28 00:38:36 【问题描述】:

我有一个名为 medias 的表,我最近在其中添加了一个名为 sort_order 的新列,类型为 Int

行值对整个表来说不是唯一的,而是它们各自的owner_user_id 字段。无论如何,我什至不在乎它们的独特性。

所有这一切的重点是允许用户设置他们上传的照片的排序顺序(最多 10 个,并且他们可以拖动和重新排序等)。当用户“删除”一张照片时,我不会删除记录,我只是在该行上将visible 字段设置为false

无论如何,我正在引入一个添加 sort_order 的迁移(他们以前无法订购照片,它们只会根据 order by created_at asc 进行排序)。

自从添加新字段后,我将新的 sort_order 设置为默认值 10(这样对于尚未更新应用程序的人来说,它向后兼容)。

我想出了这个查询:

select
  owner_user_id, 
  sort_order, rank() over (PARTITION BY owner_user_id ORDER BY sort_order asc, created_at asc) as new_sort_order 
from medias 
where visible=true 
order by sort_order asc, created_at asc;

这会吐出如下所示的内容:

 owner_user_id | sort_order | new_sort_order
---------------+------------+---------------
            76 |         10 |      1
            76 |         10 |      2
            76 |         10 |      3
            76 |         10 |      4
            76 |         10 |      5
             9 |         10 |      1
             9 |         10 |      2
             9 |         10 |      3
             9 |         10 |      4
             9 |         10 |      5
            79 |         10 |      1
            79 |         10 |      2
            87 |         10 |      1
            87 |         10 |      2
            87 |         10 |      3
            85 |         10 |      1
            90 |         10 |      1
            90 |         10 |      2
            90 |         10 |      3

此时我真正想做的就是将sort_order 设置为rank()。关于如何做到这一点的任何想法?

【问题讨论】:

【参考方案1】:

由于您没有唯一密钥,请使用ctid

update medias m
    set sort_order = new_sort_order
    from (
        select 
            ctid,
            owner_user_id, 
            sort_order, 
            row_number() over w as new_sort_order 
        from medias 
        where visible
        window w as (partition by owner_user_id order by sort_order asc, created_at asc)
    ) s
    where m.ctid = s.ctid;

注意,row_number() 可能比 rank() 更好,因为第一个从不重复。

【讨论】:

【参考方案2】:

您可以使用join,但您需要一个唯一的 ID。让我假设你有一个:

update medias m
    set sort_order = new_sort_order
    from (select m.*,
                 rank() over (PARTITION BY owner_user_id ORDER BY sort_order asc, created_at asc) as new_sort_order
          from medias m
          where visible = true
         ) mm
    where m.id = mm.id;

基本上,您是在子查询中进行计算,然后将结果连接回表以进行更新。

【讨论】:

以上是关于Postgres UPDATE 使用排名窗口函数的主要内容,如果未能解决你的问题,请参考以下文章

Postgres 窗口函数 - rank() 按 bigint 分区

WMS学习从悬浮窗的添加来看窗口的add和update

使用 Postgres 11 时,UPDATE 中不允许设置返回函数

Postgres SELECT ... FOR UPDATE 函数

使用具有不同 order by 子句的 postgres 窗口函数

如何在带有 Postgres 的动态框架中使用窗口函数中的列值?