如何在Rails Migration中将列从整数更改为datetime?

时间:2017-05-11 16:44:14

标签: mysql ruby-on-rails datetime integer migration

我有一个名为record_time的列来存储记录的时间,当前列数据类型是整数,数据保存为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

这会引发一个像这样的错误

Mysql2::Error: Incorrect datetime value: '1493178889' for column 'record_time' at row 1: ALTER TABLE `audios` CHANGE `record_time` `record_time` datetime DEFAULT NULL

提前致谢。

2 个答案:

答案 0 :(得分:3)

我完全跳过ActiveRecord来处理这类事情并在数据库中完成所有操作。一些数据库将指定如何在更改列的类型时将旧值转换为新值但我不知道如何使用MySQL执行此操作;相反,你可以手工完成:

  1. 使用新数据类型添加新列。
  2. 执行单个UPDATE以在转换日期类型时将旧值复制到新列。您可以使用MySQL的from_unixtime来实现此目的。
  3. 删除原始列。
  4. 将新列重命名为旧名称。
  5. 重建原始列上的所有索引。
  6. 将其转换为迁移:

    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对我来说似乎是合理的。您当然可以将其转换为change_columnupdate_all调用(可能使用reset_column_information调用来更新模型类)但我没有看到重点:更改列类型几乎总是如此涉及特定于数据库的代码(如果你想要高效),并且迁移本来就是临时桥梁。

答案 1 :(得分:2)

在插入之前,需要将UNIX时间戳转换为DateTime对象。您可以这样做:DateTime.strptime(<timestamp>,'%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