No commit activity in last 3 years
No release in over 3 years
Bulk write/upsert for ActiveRecord
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

activerecord-bulkwrite

Bulk insert/upsert for ActiveRecord!

ActiveRecord has no native support for bulk insert and it's very inefficient to insert bulk of rows into database by loops of Model#create, even with a transaction outside the loops. The best way to do bulk insert is to build a SQL statement:

INSERT INTO table_name (col1, col2, col3, ...) VALUES (v11, v12, v13, ...), (v21, v22, v23, ...), ...

It's about 100 times faster than loops of Model#create! This Gem is a helper for you to build such a sql statement - it takes care of type conversion and quoting for database. What's more, it suppports upsert, that is to try to insert first, and if that fails, then do update.

Requirement

PostgreSQL 9.5 and higher, since it depends on PostgreSQL 9.5's upsert feature.

Installation

gem install activerecord-bulkwrite

Usage

Suppose a table definition:

create table users (
  id integer primary key,
  name text,
  hireable bool,
  created_at date_time,
  foo text,
  bar text,
)

Bulk Insert

require "activerecord/bulkwrite"

fields = %w(id name hireable created_at)
rows = [
  [1, "Bob's", true, Time.now.utc],
  [2, nil, "false", Time.now.utc.iso8601],
  # ...
]

# The result is the effected(inserted) rows.
result = User.bulk_write(fields, rows)

The values of rows are sent to database as-is, and their to_s method is called when necessary. So here you need to pay attention to datetime values: it must be in UTC time, not in local time, since there's no time zone conversion.

Bulk Upsert

result = User.bulk_write(fields, rows, :conflict => [:id])

The statement above reinserted rows and thus failed on unique violation. But this time we specified an :conflict option meaning when the given fields conflict then do update with the rest fields. We can also explicitly specify the fields to update:

result = User.bulk_write(fields, rows, :conflict => [:id],  :update => %w(name created_at))

We can even specify conditions under which to do update:

result = User.bulk_write(fields, rows, :conflict => [:id],  :where => "users.hireable = TRUE"))

In fact, activerecord-bulkwrite's upsert depends on PostgreSQL 9.5's upsert:

INSERT INTO table_name (col1, col2, col3, ...) VALUES (v11, v12, v13), (v21, v22, v23), ...
ON CONFLICT (colX, colY, ...) DO UPDATE
SET colA = ..., colB = ..., ...
WHERE ...

Compatible Rails Versions and Test

It's tested against ActiveRecord 4.2, but should also work on 4.x as well as 5.