2010-05-06 54 views
2

以下情況:120K記錄的重新計算計數器緩存[軌道/ ActiveRecord的]

我有POI模型,該模型有許多圖片(1:N)。我想重新計算counter_cache列,因爲這些值不一致。

我試着在每個記錄中的ruby中進行迭代,但是這需要太長的時間,並且有時會因某些「分段錯誤」錯誤而退出。

所以我想知道,如果它可能做到這一點與原始的SQL查詢?

回答

8

如果,例如,你有PostPicture模型,並張貼has_many :pictures,你可以用update_all做到這一點:

Post.update_all("pictures_count=(Select count(*) from pictures where pictures.post_id=posts.id)") 
1

我找到了一個很好的解決方案上krautcomputing
它使用反射來查找項目的所有計數器緩存,SQL查詢僅查找不一致的對象並使用Rails reset_counters來清理事件。

不幸的是它僅適用於「傳統」反緩存(類名,沒有自定義計數器緩存名),所以我精是:

Rails.application.eager_load! 

ActiveRecord::Base.descendants.each do |many_class| 
    many_class.reflections.each do |name, reflection| 
    if reflection.options[:counter_cache] 
     one_class = reflection.class_name.constantize 
     one_table, many_table = [one_class, many_class].map(&:table_name) 
     # more reflections, use :inverse_of, :counter_cache etc. 
     inverse_of = reflection.options[:inverse_of] 
     counter_cache = reflection.options[:counter_cache] 
     if counter_cache === true 
     counter_cache = "#{many_table}_count" 
     inverse_of ||= many_table.to_sym 
     else 
     inverse_of ||= counter_cache.to_s.sub(/_count$/,'').to_sym 
     end 
     ids = one_class 
     .joins(inverse_of) 
     .group("#{one_table}.id") 
     .having("MAX(#{one_table}.#{counter_cache}) != COUNT(#{many_table}.id)") 
     .pluck("#{one_table}.id") 
     ids.each do |id| 
     puts "reset #{id} on #{many_table}" 
     one_class.reset_counters id, inverse_of 
     end 
    end 
    end 
end