Каноническим способом является использование reset_counter_cache
, т. Е .:
Author.find_each do |author|
Author.reset_counter_cache(author.id, :books)
end
... и вот как вы должны это делать, если эти таблицы имеют скромный размер, т.е. е. <= 1 000 000 строк. </p>
НО: для чего-либо большого это займет порядка дней, потому что для каждой строки требуется два запроса и полная реализация модели и т. Д.
Вот способ сделать это примерно на 5 порядков быстрее:
Author
.joins(:books)
.select("authors.id, authors.books_count, count(books.id) as count")
.group("authors.id")
.having("authors.books_count != count(books.id)")
.pluck(:id, :books_count, "count(books.id)")
.each_with_index do |(author_id, old_count, fixed_count), index|
puts "at index %7i: fixed author id %7i, new books_count %4i, previous count %4i" % [index, author_id, fixed_count, old_count] if index % 1000 == 0
Author.update_counters(author_id, books_count: fixed_count - old_count)
end
Также возможно сделать это напрямую в SQL, используя только один запрос, но вышеописанное сработало достаточно хорошо для меня. Обратите внимание на несколько запутанный способ использования разницы предыдущего счета с правильным: это необходимо, потому что update_counters
не позволяет установить абсолютное значение, а только увеличивает / уменьшает его. В противном случае столбец помечается только для чтения.