#ruby-on-rails #ruby
#ruby-on-rails #ruby
Вопрос:
У меня есть входящий CSV-файл с примерно 500 000 записей (csv_data). Хотите сравнить его с предыдущими данными, хранящимися в ActiveRecord (PreviousData).
В настоящее время это так далеко, но терпит неудачу, поскольку для сравнения больших наборов данных требуется много времени. Как бы мне оптимизировать это для обработки больших наборов данных?
added = csv_data.select{|item| !PreviousData.where(iden: item[:iden]).exists?}
Комментарии:
1. зачем вам нужно сравнивать его с предыдущими данными, что вы будете делать дальше (после сравнения) с входящими данными? эти детали помогут найти решение
Ответ №1:
Вы можете использовать Enumerable#each_slice, чтобы делать это по частям:
exists = csv_data.each_slice(1000).map do |chunk|
PreviousData.where(id: chunk.map { |item| item[:iden] })
.pluck(:id)
end.flatten
Это позволит выполнить один SQL-запрос на 1000 строк в файле CSV вместо одного на строку, что приведет к огромной разнице в производительности. Конечно, вы можете поиграть с размером пакета, чтобы настроить потребление памяти в зависимости от количества запросов к БД.
Вы также можете использовать with_index
, если хотите отслеживать, на каком фрагменте вы находитесь в данный момент:
exists = csv_data.each_slice(1000).with_index.map do |chunk, index|
puts "Importing chunk #{index}"
PreviousData.where(id: chunk.map { |item| item[:iden] })
.pluck(:id)
end.flatten
Если то, что вы делаете, создает записи из файла CSV, вы захотите изучить возможность использования UPSERT, если таковой имеется, и обернуть создание в одну транзакцию или выполнить пакетную ВСТАВКУ / UPSERT.
Комментарии:
1. Спасибо! это было именно то, что я искал!
Ответ №2:
Простым способом вы можете попробовать так:
stored_idens = PreviousData.pluck(:iden)
new_records = csv_data.reject { |item| stored_idens.include?(item[:iden]) }
Счастливого кодирования 🙂
Ответ №3:
Вы должны подготовить список значений для проверки, а затем одним единственным запросом проверить их все.
Я предполагаю, что :iden
этот столбец имеет значение uniq:
# Stores all the `:iden` values from your `csv_data` variable
idens = csv_data.map{ |item| item[:iden] }
# A single query checking that all of them are present in the PreviousData's table
PreviousData.where(iden: idens).exists?
Это должно привести только к одному запросу (не проверено)