web-dev-qa-db-ja.com

Rails migrationで1つの列を別の列の値に更新します

Railsアプリが数十万件のレコードを持ち、_created_at_タイムスタンプしかありません。これらのレコードを編集する機能を追加するので、テーブルに_updated_at_タイムスタンプを追加します。列を追加する移行では、すべての行を更新して、新しい_updated_at_が古い_created_at_と一致するようにします。 Railsで新しく作成された行。find(:all)を実行してレコードを反復処理することもできますが、テーブルのサイズが原因で数時間かかります。

_UPDATE table_name SET updated_at = created_at;
_

Rails raw SQLを実行するのではなくActiveRecordを使用した移行でこれを行うより良い方法はありますか?

66
jrdioko

移行を作成します

Rails g migration set_updated_at_values

そしてその中に次のように書きます:

class SetUpdatedAt < ActiveRecord::Migration
  def self.up
    Yourmodel.update_all("updated_at=created_at")
  end

  def self.down
  end
end

この方法で2つのことを達成できます

  • これは反復可能なプロセスであり、可能な展開(必要な場合)ごとに実行されます
  • これは効率的です。これ以上のルビー風のソリューションは考えられません(効率的です)。

注:activerecordを使用してクエリを書くのが困難になった場合、移行内でraw sqlを実行することもできます。以下を書いてください:

Yourmodel.connection.execute("update your_models set ... <complicated query> ...")
116
nathanvda

pdate_all を使用できます。これは、生のSQLと非常によく似ています。それはあなたが持っているすべてのオプションです。

ところで個人的に私は移行にそれほど注意を払いません。生のSQLが本当に最適なソリューションである場合があります。通常、移行コードは再利用されません。これは1回限りのアクションなので、コードの純度については気にしません。

20
Greg Dan

Gregdanが書いたように、update_all。次のようなことができます:

Model.where(...).update_all('updated_at = created_at')

最初の部分は、一般的な条件のセットです。最後の部分は、割り当てを行う方法を示しています。これにより、少なくともRails 4.でUPDATEステートメントが生成されます。

10

クエリはリスクにさらされるため、これはクエリを記述する必要なしに解決する一般的な方法です。

  class Demo < ActiveRecord::Migration
    def change
     add_column :events, :time_zone, :string
     Test.all.each do |p|
       p.update_attributes(time_zone: p.check.last.time_zone)
     end
     remove_column :sessions, :time_zone
    end
  end
0
Wilson Varghese

_Rails console_ ActiveRecord::Base.connection.execute("UPDATE TABLE_NAME SET COL2 = COL1")に対して次のコマンドを直接実行できます

たとえば、itemsテーブルのskuをitemsテーブルのremote_idで更新したいです。コマンドは次のようになります。
ActiveRecord::Base.connection.execute("UPDATE items SET sku = remote_id")

0
Sarwan Kumar