Ruby On Rails = сравнить ActiveRecord с массивом с тысячами или записями

#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?
  

Это должно привести только к одному запросу (не проверено)