Ruby on Rails:编写迁移以将列移动到另一个表,合并重复项,然后匹配ID

Mic*_*lle 3 sql activerecord ruby-on-rails database-migration ruby-on-rails-4

我坚持写这个迁移.

目前我有一个建筑物表.它将建筑物的城市列为一列.

(城市列中有重复的城市名称.)

  create_table "building", force: true do |t|
    t.string   "name"
    t.string   "city"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
Run Code Online (Sandbox Code Playgroud)

我现在已经创建了一个城市模型has_many :buildings.

现在的建筑物belongs_to :city.

我已city_idbelongs_to协会的建筑物添加了一个新列.

  create_table "city", force: true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
  add_index "city", ["name"], name: "index_cities_on_name", unique: true
Run Code Online (Sandbox Code Playgroud)

我正在尝试编写将执行以下操作的迁移:

1.将"城市"列从建筑物表格复制到城市表格中的"名称"列.

2.合并城市名称列中的所有重复城市

3.将建筑物表格中的"city_id"列与正确的城市相匹配

但我完全被卡住了...我已经在ActiveRecords文档中翻了一个小时了,我现在还不太清楚要做什么.任何帮助将非常感谢!谢谢!

got*_*tva 6

注意:对于我来说,我反对编写 SQL 查询来更新迁移中表中的数据。我更喜欢为此使用 rake 任务。您可以将代码放置在您想要的位置下方。

第一。cities使用表编写(并运行)迁移

第二。在 rake 任务或迁移中执行此代码

Building.all.each do |building|
  city = City.find_or_create(name: building.city) # http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-find_or_create_by
  building.city_id = city.id
  building.save!
end
Run Code Online (Sandbox Code Playgroud)

第三。编写删除列的迁移buildings.city

一些注意事项:

  1. 如果您有很多建筑物(例如 10000 多条记录),您可以使用find_each http://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_each
  2. 我更喜欢使用它save!来确保我会注意到某些建筑物无法保存 - 我可以手动重新检查此项目
  3. 不要belongs_to :city在第二步之前添加对 Building 的引用,因为我不确定 Rails 将尝试查找哪个列buildings.city(旧城市名称)或对象city(它将为零,因为city_id此时为零)


Pet*_*ein 5

我不会在ActiveRecord中进行数据迁移.在迁移中直接引用ActiveRecord模型类存在向前兼容性问题.作为一般规则,我试着避免id.所以我会回到SQL.

不确定你正在使用什么数据库,但这样的东西应该适用于MySQL:

execute("INSERT INTO cities(name, created_at, updated_at) SELECT DISTINCT city, NOW(), NOW() FROM buildings;")
execute("UPDATE buildings SET buildings.city_id = (SELECT city_id FROM cities where cities.name = buildings.name);")
Run Code Online (Sandbox Code Playgroud)

第一个语句初始化cities表,没有重复项,而第二个语句设置建筑物上的city_id列.

类似的语句(可能略有不同的语法)适用于其他数据库.