boss希望每个商家都有连续的订单号,从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
我计划在迁移期间运行此操作以将所有现有订单设置为根据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
结果是此操作需要10分钟来处理200个商家。旧方法在1小时内处理了大约10个商家。
答案 0 :(得分: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;
编辑:
如果您希望重新开始每个商家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;