Application monitoring that helps developers get it done.
Deploy apps to servers that you own and control.
Typically, when creating records in a Rails application, you want to check validations and run callbacks. However, if you need to insert hundreds of thousands of records quickly, that extra overhead can slow things down.
In this video, we’ll explore how to insert records efficiently without triggering validations or callbacks, making bulk inserts significantly faster.
Jumping back into our IRB session playground, we have our posts table with title and body columns, along with a Post model that includes our validation and callback from the previous video.
Let’s set this up and look at a new method for inserting data:
result = Post.insert({ title: "the title", body: "some text" })
Instead of passing our attributes directly, we have to pass a hash of attributes and their values.
Running this, we see several key differences in the SQL logs:
1. Direct SQL Execution
2. No Validations Are Run
If we run a test of trying to insert an empty record, it runs a SQL query.
result = Post.insert({})
However, it has not checked our validations. Even though our post model has said that we need to ensure that the title attribute is present, when we use the insert method without a title, it's completely fine. There's not a database constraint saying there must be a title, that is only a constraint at the application level.
3. No Callbacks Are Triggered
Callbacks like after_create_commit are not executed because insert doesn’t wrap the operation in a transaction. Even if we add another callback, like after_create, it still won’t run.
If you need to insert thousands of records, iterating over each one individually is inefficient. Instead, use insert_all, which inserts multiple records in a single SQL query:
Post.insert_all([
{ title: "title one" },
{ title: "title two" }
])
The insert_all method takes an array of hashes of your attributes.
This generates one SQL statement that inserts multiple rows at once, significantly improving performance.
You might notice that insert statements include an ON CONFLICT DO NOTHING clause, which we did not see with create.
Now that we know the three major ways to insert records into an SQLite database (create, save, and insert), here’s when to use each:
Method | Runs Validations? | Runs Callbacks? | Best Use Case |
---|---|---|---|
create! | ✅ Yes | ✅ Yes | Default method for most inserts |
save | ✅ Yes | ✅ Yes | When updating existing objects |
insert | ❌ No | ❌ No | Bulk inserts when performance is critical |
Best Practice:
Now that we’ve covered efficient data insertion, our next focus will be updating existing records in the database.