Postgres UPDATE using rank window function

别等时光非礼了梦想. 提交于 2021-02-16 13:58:13

问题


I have a table called medias where I've just recently added a new column to called sort_order of type Int.

The row values will not be unique to the entire table, but to their own respective owner_user_id fields. Regardless, I don't even care about the uniqueness of them tbh.

The point of all this is to allow users to set the sort order of the photos they upload (up to 10, and they can drag and re-order, etc). When the user "deletes" a photo, I don't remove the record, I simply set a visible field to false on that row.

Aaaanyway, I'm introducing a migration that adds sort_order (they used to not be able to order the photos, they would just be sorted according to order by created_at asc).

Since adding the new field, I've made the new sort_order have a default value of 10 (so that it's backwards compatible for folks who haven't updated the application).

I was able to come up with this query:

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;

This spits out something that looks like the following:

 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

at this point all i really want to do is set that sort_order to that rank(). Any thoughts on how to do this?


回答1:


As you do not have a unique key, use 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;

Note, row_number() may be better than rank() as the first never gives duplicates.




回答2:


You can use a join, but you need a unique id. Let me assume that you have one:

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;

Basically, you are doing the calculation in a subquery and then joining the result back to the table for the update.



来源:https://stackoverflow.com/questions/40296265/postgres-update-using-rank-window-function

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!