`insert_all` and `upsert_all` ActiveRecord methods

TIL that since Rails 6 there have been quite new ActiveRecord bulk methods:
insert_all and upsert_all. To better understand their powers, let’s quickly refresh our knowledge about INSERT and UPSERT SQL operations:

  1. INSERT is used to insert new records in a table.
  2. UPSERT either inserts or updates a row if it already exists.

How do we perform the above operations within Rails applications usually? Well, there are create and update methods.

To my surprise, there are also insert and upsert ones 😲. According to the Rails documentation:

  1. insert inserts a single record into the database in a single SQL INSERT statement. It does not instantiate any models nor does it trigger Active Record callbacks or validations. Though passed values go through Active Record’s type casting and serialization.
  2. upsert updates or inserts (upserts) a single record into the database in a single SQL INSERT statement. It does not instantiate any models nor does it trigger Active Record callbacks or validations. Though passed values go through Active Record’s type casting and serialization.

What is even more interesting, they call the insert_all and upsert_all methods:

# File activerecord/lib/active_record/persistence.rb, line 66
def insert(attributes, returning: nil, unique_by: nil)
  insert_all([ attributes ], returning: returning, unique_by: unique_by)
end
# File activerecord/lib/active_record/persistence.rb, line 187
def upsert(attributes, returning: nil, unique_by: nil)
  upsert_all([ attributes ], returning: returning, unique_by: unique_by)
end

Example usage:

Book.insert_all([
  { id: 1, title: "Rework", author: "David" },
  { id: 2, title: "Eloquent Ruby", author: "Russ" }
])

The methods insert and/or update all the records using a single SQL query. They also do not instantiate any models nor do trigger Active Record callbacks and validations. What does it mean for us? They are much faster, especially for bulk operations, when ActiveRecord callbacks and validations can be skipped.

The insert methods have bang equivalents, insert! and insert_all!. They raise an exception when any row violates unique index on the table.

Bonus: If you are interested in some basic benchmarks you can find them in the PR which added the methods.

 

Igor Springer

I build web apps. From time to time I put my thoughts on paper. I hope that some of them will be valuable for you. To teach is to learn twice.

 

Leave a Reply

Your email address will not be published. Required fields are marked *