简体   繁体   中英

How to bulk insert data in a table using Rails and a single insert statement

I have the following ActiveRecord models, Widget, Merchant, Store, StoresWidget (many-to-many association)

class Merchant < ActiveRecord::Base
  has_many :stores
  has_many :widgets
end

class Widget < ActiveRecord::Base
  has_many :stores_widgets
  has_many :stores, :through => :stores_widgets
  belongs_to :merchant
end

class Store < ActiveRecord::Base
  has_many :stores_widgets
  has_many :widgets, :through => :stores_widgets
  belongs_to :merchant
end

class StoresWidget < ActiveRecord::Base
  belongs_to :widget
  belongs_to :store
end

So the corresponding tables are widgets , merchants , stores and stores_widgets , where widgets and stores each has a id column and stores_widgets has two columns store_id and widget_id . A Widget can be available in 1 or more stores and a store can have many widgets available. Some widgets are available in all stores some are only available in a sub-set of stores. If a Widget is restricted to a subset of stores, the column restricted is true

When a new store is added to a merchant I want to update all the unrestricted widgets to be associated with that store. Ideally I would like to have code like this in my StoresController#create

class StoresController < ApplicationController
  def create
    # build new store...
    Store.transaction do
      store.save!
      Widget.update_all_unrestricted_widgets_with_store(store)
    end
    render :show
  end
end

Where update_all_unrestricted_widgets_with_store ends up executing SQL like:

INSERT INTO stores_widgets (store_id, widget_id) 
(SELECT #{store.id}, widgets.id 
 FROM widgets
 WHERE widgets.merchant_id = #{store.merchant_id} 
 AND widgets.restricted = FALSE)

So if a merchant has 100000 widgets that are unrestricted a 100000 new stores_widgets rows are created in one INSERT rather than 100000 distinct INSERTS.

Preferably I would like to get ActiveRecord to build an insert like this. Is that possible? If not would I be able to do this with ARel? I want to avoid executing a SQL string to achieve this if possible, so that I can maintain a level if separation between the code and the database SQL syntax.

You can use activerecord-import implements AR#import

activerecord-import is a library for bulk inserting data using ActiveRecord.

see how it works:

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

Reference - https://github.com/zdennis/activerecord-import

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM