【发布时间】:2018-11-14 22:27:11
【问题描述】:
我有一个迁移,它使用新的属性值更新现有记录。该模型称为“MyRecord”。它在数据库中有数百万条记录,新的 unit_id 列为空。我想用特定值更新该 unit_id 列:
MyRecord.find_each do |record|
unit_id = Unit.calculate_unit_from_old_columns(record.legacy_column_1, record.legacy_column_2).first.id
record.update unit_id: unit_id
end
这会创建很多 N+1 查询:
SELECT units.* FROM units WHERE units.item_1 = 'Electronics' AND units.item_2 = 'Auto'
UPDATE my_records SET unit_id='43' WHERE legacy_column_1 = 'Legacy Electronics' AND legacy_column_2 = 'Legacy Auto';
其中一些 N+1 查询是重复的。我在日志中看到了很多这样的内容:
SELECT units.* FROM units WHERE units.item_1 = 'Electronics' AND units.item_2 = 'Auto'
SELECT units.* FROM units WHERE units.item_1 = 'Electronics' AND units.item_2 = 'Auto'
我熟悉通过包含进行急切加载。但是,当运行此迁移以更新现有数据时,还没有关联。所以我不能这样做:
record.includes(:unit)
如何消除 N+1 个查询并缓存查询,以便在重复查询时不会再次访问数据库?
【问题讨论】:
-
calculate_unit_from_old_columns有多复杂?最好将所有内容移动到更新查询(SET unit_id = DO_STUFF(legacy_column_1, legacy_column_2)。如果太复杂,您应该准备批量更新(将 1000 条记录映射到(id, new_unit_id)对,并将其用于更新查询。 -
@MarcinKołodziej 你能给我一个批量更新的例子或指向一个链接吗?
-
calculate_unit_from_old_columns 是一个命名范围:scope : calculate_unit_from_old_columns, ->(item1, item2) { where(item_1: item1, item2: item_2 ) }
-
哦,对了,这行很长。好吧,你可以用 join 写一个简单的更新。你用 MySQL 和 PostgreSQL 标记了你的问题,它们有不同的语法,应该很容易搜索。
-
@MarcinKołodziej 我正在使用 MySQL
标签: mysql sql ruby-on-rails