如何快速批量更新postgres中的序列号

Hom*_*man 5 sql postgresql ruby-on-rails

老板想要每个商家的连续订单号,从1000开始.

现在我正在遍历每个商家(使用ruby),并更新这样的订单:

#running all of this in a migration
add_column :orders, :order_seq, :integer


Merchant.find_each do |merchant|
  order_seq = 999
  merchant.orders.order(:ordered_at).find_each do |order|
    order.update_column(:order_seq, order_seq+=1)
  end
end
Run Code Online (Sandbox Code Playgroud)

我计划在迁移期间运行此命令,以将所有现有订单设置为根据ordered_at日期填充序列号.我在生产数据库的一个分支上测试了它,每个订单更新平均需要80毫秒.有近百万订单记录,这将导致太多的停机时间.

使用原生postgres有更快的方法吗?这将是一次性迁移,需要运行一次,并且没有其他任何内容同时进行.

我不是postgres专家,但有没有办法在每个merchant_id上使用999 + row_number()的窗口函数并将row_number保存回order_seq列?

编辑:

使用@ Gorden-Linoff答案,但略有修改.我意识到我不需要在merchant_id上使用分区,因为只有一些活跃的商家需要这个,而不是整个表.此外,更新需要在订单表上,而不是商家表,而where子句只能使用id而不是merchant_id和ordered_at.

最终解决方案

  Merchant.active.find_each(batch_size: 100) do |merchant|
    statement = "update orders set order_seq = o.seqnum + 999 " +
      "from (select o.id, row_number() " +
      " over (order by ordered_at) as seqnum from orders o where o.merchant_id = #{merchant.id}" +
      ") o where orders.id = o.id"
    ActiveRecord::Base.connection.execute(statement)
  end
Run Code Online (Sandbox Code Playgroud)

结果是该操作需要10分钟来处理200个商家.旧方法在1小时内处理了大约10个商家.

Gor*_*off 7

我认为您可以使用可更新的子查询使用本机Postgres执行此操作:

update merchants
    set order_seq = m.seqnum + 999
    from (select m.*, row_number() over (order by ordered_at) as seqnum
          from merchants m
         ) m
    where merchants.merchant_id = m.merchant_id and
          merchants.ordered_at = m.ordered_at;
Run Code Online (Sandbox Code Playgroud)

编辑:

如果您希望重新开始每个商家ID,那么只需使用partition by:

update merchants
    set order_seq = m.seqnum + 999
    from (select m.*, row_number() over (partition by merchant_id
                                         order by ordered_at
                                        ) as seqnum
          from merchants m
         ) m
    where merchants.merchant_id = m.merchant_id and
          merchants.ordered_at = m.ordered_at;
Run Code Online (Sandbox Code Playgroud)