29

Я хочу сделать пакетную вставку из нескольких тысяч записей в базу данных (POSTGRES в моем случае) из моего приложения Rails.Пакетная вставка в рельсах 3

Каким будет «Rails way» этого? Что-то быстрое, а также правильный способ сделать это.

Я знаю, что могу создать SQL-запрос путем конкатенации строк, но мне нужен лучший подход.

+0

Смотрите также: [Как реализовать массовую вставку в Rails 3] (http://stackoverflow.com/questions/8505263/how-to-implement-bulk-insert-in-rails-3) и [Массовая вставка записей в таблицу Active Record] (http://stackoverflow.com/questions/ 15317837/объемной вставка-запись-в-активная запись стол). – 2014-04-13 05:06:17

ответ

49

ActiveRecord .create метод поддерживает массовое создание. Метод эмулирует эту функцию, если БД ее не поддерживает и использует базовый механизм БД, если эта функция поддерживается.

Просто передайте множество вариантов.

# Create an Array of new objects 
User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) 

Блок поддерживается, и это общий способ для общих атрибутов.

# Creating an Array of new objects using a block, where the block is executed for each object: 
User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u| 
    u.is_admin = false 
end 
+0

. Как вы думаете, для случая postgres он создаст один оператор insert? – phoenixwizard

+2

Возможно, это зависит от версии драйвера и версии PG. Вы можете попробовать его в консоли и посмотреть исполняемый оператор SQL. –

+2

Кажется, он создает отдельные запросы. Хотя окружающая его транзакция, похоже, делает ее быстрее. В любом случае, я могу обеспечить единый запрос вставить? – phoenixwizard

1

Вы можете создать сценарий в модели рельсы, напишите ваши запросы для вставки в этом сценарии В рельсам можно запустить сценарий с помощью

rails runner MyModelName.my_method_name 

Это лучший способ, который я использовал в моем проекте.

Update:

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

user_string = " ('[email protected]','a'), ('[email protected]','b')" 
User.connection.insert("INSERT INTO users (email, name) VALUES"+user_string) 

для нескольких записей:

new_records = [ 
    {:column => 'value', :column2 => 'value'}, 
    {:column => 'value', :column2 => 'value'} 
] 

MyModel.create(new_records) 
+0

Я ищу что-то вроде вставки 1000 объектов в базу данных в одном запросе. USe case: Я регистрирую пользователя с facebook и сохраняю все его друзья в facebook с помощью одного вызова БД. – phoenixwizard

+0

Эй, @ Арам Бхусаль, см. Мой обновленный ответ. –

+0

Это именно то, чего я хотел избежать :) Кажется, это либо так, либо медленнее ... – phoenixwizard

0

Вы можете сделать это быстрый путь или путь Rails;) лучший способ в моем опыте импортировать массивные данные в Postgres через CSV. Что займет несколько минут, путь Rails займет несколько секунд, используя встроенную функцию импорта CSV Postgres.

http://www.postgresql.org/docs/9.2/static/sql-copy.html

Он даже запускает триггеры базы данных и уважает ограничения базы данных.

Редактировать (после вашего комментария): Gotcha. В этом случае вы правильно описали свои два варианта. Раньше я был в той же ситуации, реализовал его, используя Rails 1000 save! потому что это была простейшая вещь, которая работала, а затем оптимизировала ее для стратегии «добавить огромную строку запроса», поскольку она была на порядок лучше.

Конечно, преждевременная оптимизация - это корень всего зла, поэтому, возможно, это простой медленный путь Rails и знаю, что построение большой строки запроса является совершенно законным методом оптимизации за счет поддержания работоспособности. Я чувствую, что ваш реальный вопрос: «Есть ли способ Railsy, ​​который не включает 1000 запросов?» - К сожалению, ответ на этот вопрос - нет.

+0

Я думаю, что мой вопрос непонятен. Я думаю о том, что пользователь вводит логин в свою учетную запись на facebook. Я спасаю всех своих друзей. Я ожидаю от 1000 до 4000 записей за раз. Я хочу сделать это из моего приложения Rails – phoenixwizard

17

Я, наконец, достиг решения после двух ответов @ Симоне Карлетти и @Sumit Munot.

До тех пор, пока драйвер postgres не поддерживает массовую вставку метода ActiveRecord .create, я хотел бы пойти с activerecord-import gem. Это делает объемную вставку, и это тоже в одном заявлении вставки.

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

В POSTGRES это приводит к одному статусу вставки.

После того, как Postgres драйвер поддерживает массовую вставку метода .create ActiveRecord в одном операторе вставки, а затем раствор @Simone Carletti «s имеет больше смысла :)

+1

К сожалению, это по-прежнему лучшее решение для такой простой проблемы. Решения «write raw sql» работают и могут быть проще для одного, но если вы делаете это более одного раза, то этот драгоценный камень - лучшее решение, чтобы не посыпать необработанные sql (или другие языки) повсюду. –

+0

Он говорит, что метод отсутствует - postgres-9.4, Ubuntu 14.04, rails 4.2 – Anwar

+1

@Anwar: У вас есть «gem» activerecord-import'' в вашем Gemfile? Это необходимо для использования 'Model # import'. – Pete

Смежные вопросы