我有一个事件表和会话表。事件has_many会话,这是关联。现在我想将time_zone列从sessions表移到事件表。那么我如何在迁移的帮助下做到这一点。如何将sessions表中time_zone的现有记录移动到事件表?
答案 0 :(得分:3)
首先,您需要确保与同一事件关联的会话具有相同的时区。你可以这样做:
Session.group(:event_id).count(:time_zone)
这将返回一个哈希映射event_id
到与之关联的时区数。这个数字应该总是一个。
其次,我建议您首先添加events.time_zone
并开始使用它,并在新代码投入生产一段时间后在单独的迁移中删除sessions.time_zone
并证明可以正常工作。
第三,添加events.time_zone
的迁移应该如下所示(为了清晰起见,我添加了一些注释):
class AddTimeZoneToEvents < ActiveRecord::Migration
class Event < ActiveRecord::Base; end
class Session < ActiveRecord::Base; end
def up
# Add a NULLable time_zone column to events. Even if the column should be
# non-NULLable, we first allow NULLs and will set the appropriate values
# in the next step.
add_column :events, :time_zone, :string
# Ensure the new column is visible.
Event.reset_column_information
# Iterate over events in batches. Use #update_columns to set the newly
# added time_zone without modifying updated_at. If you want to update
# updated_at you have at least two options:
#
# 1. Set it to the time at which the migration is run. In this case, just
# replace #update_columns with #update!
# 2. Set it to the maximum of `events.updated_at` and
# `sessions.updated_at`.
#
# Also, if your database is huge you may consider a different query to
# perform the update (it also depends on your database).
Event.find_each do |event|
session = Session.where(event_id: event.id).last
event.update_columns(time_zone: session.time_zone)
end
# If events don't always need to have time zone information then
# you can remove the line below.
change_column_null :events, :time_zone, false
end
def down
remove_column :events, :time_zone
end
end
请注意,我在迁移中重新定义了模型。这样做至关重要,因为:
确定您的更改按预期工作后,您可以删除sessions.time_zone
。如果出现问题,您可以简单地回滚上述迁移并轻松恢复工作版本。
答案 1 :(得分:0)
您只需使用以下迁移。
class Test < ActiveRecord::Migration
def change
add_column :events, :time_zone, :string
Event.all.each do |e|
e.update_attributes(time_zone: e.sessions.last.time_zone)
end
remove_column :sessions, :time_zone
end
end