web-dev-qa-db-fra.com

Mettre à jour une colonne à la valeur d'une autre dans Rails migration

J'ai une table dans une application Rails avec des centaines de milliers d'enregistrements, et ils n'ont qu'un horodatage created_at. J'ajoute la possibilité de modifier ces enregistrements, donc je Je veux ajouter un horodatage updated_at à la table. Dans ma migration pour ajouter la colonne, je veux mettre à jour toutes les lignes pour que le nouveau updated_at corresponde à l'ancien created_at, car c'est la valeur par défaut pour les lignes nouvellement créées dans Rails. Je pourrais faire une find(:all) et parcourir les enregistrements, mais cela prendrait des heures en raison de la taille de la table. Ce que je veux vraiment faire c'est:

UPDATE table_name SET updated_at = created_at;

Existe-t-il une meilleure façon de le faire dans une migration Rails en utilisant ActiveRecord plutôt que d'exécuter du SQL brut?

66
jrdioko

Je créerais une migration

Rails g migration set_updated_at_values

et à l'intérieur, écrivez quelque chose comme:

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

  def self.down
  end
end

De cette façon, vous réalisez deux choses

  • c'est un processus reproductible, avec chaque déploiement possible (si nécessaire) il est exécuté
  • c'est efficace. Je ne peux pas penser à une solution plus rubyesque (qui soit aussi efficace).

Remarque: vous pouvez également exécuter SQL brut dans une migration, si la requête devient trop difficile à écrire en utilisant Activerecord. Écrivez simplement ce qui suit:

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

Vous pouvez utiliser pdate_all qui fonctionne de manière très similaire au SQL brut. C'est toutes les options que vous avez.

BTW personnellement je ne fais pas trop attention aux migrations. Parfois, le SQL brut est vraiment la meilleure solution. Généralement, le code de migration n'est pas réutilisé. Il s'agit d'une action unique, donc je ne me soucie pas de la pureté du code.

20
Greg Dan

Comme l'a écrit gregdan, vous pouvez utiliser update_all. Vous pouvez faire quelque chose comme ça:

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

La première partie est votre ensemble typique de conditions. La dernière partie indique comment effectuer les affectations. Cela produira une instruction UPDATE, au moins dans Rails 4.

10
Martin Streicher

Il s'agit d'une façon générale de résoudre, sans avoir besoin de Writing Query, car les requêtes sont soumises à des risques.

  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

Vous pouvez exécuter directement la commande suivante sur votre Rails consoleActiveRecord::Base.connection.execute("UPDATE TABLE_NAME SET COL2 = COL1")

Par exemple: je veux mettre à jour le sku de ma table d'articles avec remote_id des tables d'articles. la commande sera la suivante:
ActiveRecord::Base.connection.execute("UPDATE items SET sku = remote_id")

0
Sarwan Kumar