2011-12-14 1 views
22

Мне нужно вставить массив писем в разные записи в таблицу контактов. Как это может быть сделано.Как реализовать объемную вставку в Rails 3

Eg: @email = ["[email protected]", "[email protected]", "[email protected]", ... ] 

Я не хочу использовать.

@email.each do |email| 
    @contact = Contact.new 
    @contact.email = email 
    @contact.save 
    end 

Эта причина n вставляет запросы. Мне просто нужен один запрос на вставку, чтобы вставить эти значения. Как это можно сделать в rails 3.0.9 (и в идеале MySQL). Пожалуйста, помогите

+0

Смотри также: [Объемные вставки записей в активную таблицу Record] (http://stackoverflow.com/questions/15317837/bulk-insert-records-into-active-record-table) и [вставки в Пакетный рельсы 3] (http://stackoverflow.com/questions/15784305/batch-insertion-in-rails-3). –

ответ

47

ActiveRecord-импорт реализует AR # импорт

ActiveRecord-импорт библиотеки для данных насыпной вставив с использованием ActiveRecord.

посмотреть, как это работает: дом

books = [] 
10.times do |i| 
    books << Book.new(:name => "book #{i}") 
end 
Book.import books 

проекта на Github и это wiki.

+6

Это фантастический драгоценный камень, я рад, что вы это указали! –

+1

Значительно увеличивает объемную вставку. –

+0

Также, связанный с этим вопрос: http://stackoverflow.com/questions/13718013/how-do-i-retrieve-a-list-of-created-ids-for-bulk-insert-in-active-record, см. второй ответ (не принятый). Исходная версия SQL возвращает все идентификаторы. – Rob

4

Вы также можете попробовать upsert, что примерно так же быстро, как activerecord-import, но только работает (в настоящее время) с MySQL, Postgres и SQLite3:

require 'upsert' 
Upsert.batch(Contact.connection, Contact.table_name) do |upsert| 
    emails.each do |email| 
    upsert.row(email: email) 
    end 
end 

Обратите внимание, что это включает в себя один запрос к базе данных для каждой записи, но это «upsert», поэтому вам не нужно проверять, существует ли запись. В вашем примере это не проблема, но в большинстве приложений она становится одной из них.

+0

Наличие одного запроса базы данных на запись в точности не является массовым импортом. Массовый импорт имеет один вставной оператор SQL с множеством наборов параметров, привязанных к нему, так что у вас есть только одна база данных в оба конца, и, следовательно, это быстро. – Rob

+0

'upsert' библиотека начиналась с использования' INSERT ON DUPLICATE KEY UPDATE', который является массовым. –

+0

(oops, hit enter, прежде чем я смогу полностью объяснить себя), даже если это не делает этого больше, это делает 1 поездку на db за запись, я думаю, что исторический факт - это то, почему я бездумно не оправдал, как 'upsert' является, на мой взгляд, формой массового обновления. я бы определил массовое обновление как «установив новое состояние по многим записям без учета их текущего состояния» - сколько db-переходов занимает важная деталь. «upserting» - это особый способ делать массу. см. https://github.com/seamusabshere/upsert/commit/b8365d91fe68e450382119, почему я прекратил использовать метод 1-trip-per-batch. –

2

Самый простой способ без дополнительного драгоценного камня является Concat string и выполнить его в одной вставке SQL (http://www.electrictoolbox.com/mysql-insert-multiple-records/).

@email = ["[email protected]", "[email protected]", "[email protected]"] 

time = Time.current.to_s(:db) 

values = @email.map do |email| 
    "('#{email}', '#{time}', '#{time}')" 
end 

sql = "INSERT INTO contacts (email, created_at, updated_at) VALUES #{values.join(', ')}" 
Contact.connection.execute(sql) 
+0

Я реализовал это, но теперь мне интересно, если он подвержен риску внедрения SQL, если вы используете что-то более свободную, чем электронную почту. – Nuclearman

+0

@Nuclearman, у меня такое же ощущение относительно SQL-инъекции –