2017-10-03 2 views
0

j'ai deux classes:Rails: Mise à jour tous, mais le plus récemment créé pour chacun dans un grand nombre a

class Account 
    has_many :follow_ups 
end 

class FollowUp 
    belongs_to :account 
end 

Pour chaque compte, je dois introduire une colonne completed_at sur FollowUp. Je dois également effectuer les opérations suivantes pour chaque compte: définissez completed_at au 1er janvier 1900 pour chaque suivi, à l'exception du suivi créé le plus récemment.

J'ai essayé de mettre ce qui suit dans un seul fichier de migration, mais il a tout laissé FollowUp s 'completed_at comme nil. configuration

class AddCompletedAtToFollowUps < ActiveRecord::Migration[5.1] 
    def change 
    add_column :follow_ups, :completed_at, :datetime 

    set_all_but_most_recent_follow_ups_as_long_completed_for_each_account 
    end 

    private 

    def set_all_but_most_recent_follow_ups_as_long_completed_for_each_account 
    Account.all.each do |account| 
     all_but_most_recent_follow_up_for(account).find_each do |follow_up| 
     follow_up.update(completed_at: Time.utc(1900)) 
     end 
    end 
    end 

    def all_but_most_recent_follow_up_for(account) 
    account.follow_ups.order(created_at: :desc).offset(1) 
    end 
end 

Je sais que c'est un O affreux (n^2), mais ce que je suis surpris est que cela ne fonctionne même pas.

Quelqu'un peut-il m'aider à deviner la requête la plus rapide pour accomplir cela?

P.S. all_but_most_recent_follow_up_for(account).update_all mis à jour tous du FollowUps, ce qui est également incorrect.

Répondre

0

Ce qui suit semble fonctionner assez bien. (Mais je ne pense qu'il ya quelque chose de plus efficace, peut-être quelque chose qui évite l'itération.)

class AddCompletedAtToFollowUps < ActiveRecord::Migration[5.1] 
    def change 
    add_column :follow_ups, :completed_at, :datetime 

    set_all_but_most_recent_follow_ups_as_long_completed_for_each_account 
    end 

    private 

    def set_all_but_most_recent_follow_ups_as_long_completed_for_each_account 
    Account.all.find_each do |account| 
     follow_ups = account.follow_ups 
     count = follow_ups.count 

     next unless count > 1 

     follow_ups.order(created_at: :asc).limit(count - 1). 
     update_all(completed_at: Time.utc(1900)) 
    end 
    end 
end 
0

Si vous ne me dérange pas utiliser beaucoup de SQL, vous pouvez utiliser une clause OFFSET:

class AddCompletedAtToFollowUps < ActiveRecord::Migration[5.1] 
    def change 
    add_column :follow_ups, :completed_at, :datetime 
    Account.find_each do |a| 
     FollowUp.update_all("completed_at = '#{Time.utc(1900)}' where id in (select id from follow_ups where account_id = #{a.id} order by created_at desc offset 1)") 
    end 
    end 
end