以下情況:120K記錄的重新計算計數器緩存[軌道/ ActiveRecord的]
我有POI模型,該模型有許多圖片(1:N)。我想重新計算counter_cache列,因爲這些值不一致。
我試着在每個記錄中的ruby中進行迭代,但是這需要太長的時間,並且有時會因某些「分段錯誤」錯誤而退出。
所以我想知道,如果它可能做到這一點與原始的SQL查詢?
以下情況:120K記錄的重新計算計數器緩存[軌道/ ActiveRecord的]
我有POI模型,該模型有許多圖片(1:N)。我想重新計算counter_cache列,因爲這些值不一致。
我試着在每個記錄中的ruby中進行迭代,但是這需要太長的時間,並且有時會因某些「分段錯誤」錯誤而退出。
所以我想知道,如果它可能做到這一點與原始的SQL查詢?
如果,例如,你有Post
和Picture
模型,並張貼has_many :pictures
,你可以用update_all
做到這一點:
Post.update_all("pictures_count=(Select count(*) from pictures where pictures.post_id=posts.id)")
我找到了一個很好的解決方案上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