Mysql 在Rails迁移中如何将列从整数改为datetime?

Mysql 在Rails迁移中如何将列从整数改为datetime?,mysql,ruby-on-rails,datetime,integer,migration,Mysql,Ruby On Rails,Datetime,Integer,Migration,我有一个名为record_time的列来存储记录的时间当前列数据类型为integer,数据保存为unix时间戳现在我想找到一种方法将此unix时间戳转换为datetime字段,而不会丢失该列中的数据。现在,我创建了一个迁移文件,如下所示: class ChangeRecordTimeToDatetime < ActiveRecord::Migration def up as = Audio.all.map {|a| {id: a.id, record_time: Time.at

我有一个名为record_time的列来存储记录的时间当前列数据类型为integer,数据保存为unix时间戳现在我想找到一种方法将此unix时间戳转换为datetime字段,而不会丢失该列中的数据。现在,我创建了一个迁移文件,如下所示:

class ChangeRecordTimeToDatetime < ActiveRecord::Migration
  def up
    as = Audio.all.map {|a| {id: a.id, record_time: Time.at(a.record_time)}}
    Audio.all.update_all("record_time = NULL")
    change_column :audios, :record_time, :datetime
    as.map {|a| Audio.find(a[:id]).update(record_time: a[:record_time])}
  end

  def down
    as = Audio.all.map {|a| {id: a.id, record_time: a.record_time.to_i}}
    Audio.all.update_all("record_time = NULL")
    change_column :audios, :record_time, :integer
    as.map {|a| Audio.find(a[:id]).update(record_time: a[:record_time])}
  end
end

提前感谢。

在插入UNIX时间戳之前,需要将其转换为DateTime对象。您可以这样做:DateTime.strtime,'%s'

因此,要将此应用于您的问题,请尝试以下方法:

  def up
    as = Audio.all.map {|a| {id: a.id, record_time: DateTime.strptime(a.record_time.to_s, '%s')}}
    remove_column :audios, :record_time
    add_column :audios, :record_time, :datetime
    as.map {|a| Audio.find(a[:id]).update(record_time: a[:record_time])}
  end

对于这类事情,我会完全跳过ActiveRecord,并在数据库中完成。一些数据库允许在更改列的类型时指定如何将旧值转换为新值,但我不知道如何使用MySQL实现这一点;相反,您可以手动完成:

添加具有新数据类型的新列。 在转换日期类型时,执行一次更新,将旧值复制到新列。您可以使用MySQL来实现这一点。 删除原始列。 将新列重命名为旧名称。 重建原始列上的所有索引。 将其转化为迁移:

def up
  connection.execute(%q{
    alter table audios
    add record_time_tmp datetime
  })
  connection.execute(%q{
    update audios
    set record_time_tmp = from_unixtime(record_time)
  })
  connection.execute(%q{
    alter table audios
    drop column record_time
  })
  connection.execute(%q{
    alter table audios
    change record_time_tmp record_time datetime
  })
  # Add indexes and what not...
end

在这里,您非常熟悉特定于数据库的代码,因此使用直接SQL对我来说似乎是合理的。当然,您可以将其转换为更改列并更新所有调用(可能是使用更新模型类的调用),但我不认为这有什么意义:更改列类型几乎总是涉及特定于数据库的代码,如果您想提高效率,那么迁移就意味着临时桥接。

您需要转换UNIX时间戳在插入对象之前删除它们。您可以这样做:DateTime.strptime1493178889,'%s'谢谢,我现在使用的是Time.at1493178889。DateTime.strptime1493178889,'%s'和Time.at1493178889之间有什么区别吗?只是为了更清楚。DateTime.strptime返回一个DateTime对象。Time.at返回一个时间。因为您的列是DATETIME,所以在可能的情况下,您希望使用Ruby DATETIME。
def up
  connection.execute(%q{
    alter table audios
    add record_time_tmp datetime
  })
  connection.execute(%q{
    update audios
    set record_time_tmp = from_unixtime(record_time)
  })
  connection.execute(%q{
    alter table audios
    drop column record_time
  })
  connection.execute(%q{
    alter table audios
    change record_time_tmp record_time datetime
  })
  # Add indexes and what not...
end