How ActiveRecord decides when to preload and when eager load records

Paweł Dąbrowski - Chief Technology Officer
rails, ruby, software, web development

Which two methods do Active Record use under the hood of the includes method? Only a small percentage of the developers participating in our Mortal Coding programming tournament knew the answer to this question. Therefore we decided to write an article explaining this issue. What’s the correct answer? And why is it important in daily development?

Which two methods do Active Record use under the hood of the includes method?

a. preload and eager_load
b. preload and joins
c. references and eager_load
d. joins and eager_load

The correct answer is a. Unfortunately, only 34% of developers answered correctly. Therefore, we decided to write an article explaining this topic and the correct answer because this knowledge is valuable in daily development.

Curious how Active Record handles includes method under the hood? Let’s see.

What is preloading in Active Record

As the name states, Active Record can pre-load records from the given association. Such a process is beneficial when you want to access the association’s records from the view.

With preload, Rails will always generate two separated SQL queries:

User.preload(:tests)
# SELECT "users".* FROM "users"
# SELECT "tests".* FROM "tests" WHERE "tests"."user_id" IN ($1, $2, $3, $4, $5)

You can now access tests for the given user without calling the database again. However, when using preload, you can’t refer to the association in the query. For example, the following code will throw an error:

User.preload(:tests).where(tests: { status: :added })

It happens because we load the data in two separated queries; it’s not a join type of query.

What is eager loading in Active Record

Eager loading solves the error raised when using preload and referring to the association in the query. It solves this problem because Rails uses the JOIN clause (left outer join to be more specific) to connect the data:

User.eager_load(:tests)
# SELECT "users"."id" AS t0_r0, "users"."email" AS t0_r1, ... FROM "users" LEFT OUTER JOIN "tests" ON "tests"."user_id" = "users"."id"

We can now easily update our query and refer to the tests table without getting an error:

User.eager_load(:tests).where(tests: { status: :added })

Use eager load if you don’t want to load all associated records and you want to narrow the result using some type of criteria on the query level.

How includes is working in Active Record

Includes connects those two methods, eager_load and preload that we discussed before. By the default, it works like preload, so those two invocations produce the same SQL query:

User.preload(:tests)

# is the same as

User.includes(:tests)

However, when you would refer to the included association in the query, includes will use eager_load to make it possible:

User.eager_load(:tests).where(tests: { status: :added })

# is the same as

User.includes(:tests).where(tests: { status: :added })

If you use includes, Rails choose for you, but nothing prevents you from making a decision for yourself and deciding whether to use eager_load or preload.

The right answer

The includes method from the Active Record library decides whether to use the preload method to load data in two separated queries or use eager_load to load the data in one query with a left outer join.

Soon we will publish more articles regarding the hardest questions from the Mortal Coding challenge. Stay tuned!

If you are looking for an experienced development Ruby on Rails team, let’s talk about how we can help you empower your teams with top-notch engineers!


Let’s get in touch
Author's Bio
Paweł Dąbrowski

Chief Technology Officer

Open source fan and growth seeker with over a decade of experience in writing for both human beings and computers. I connect the dots to create high-quality software, valuable relations with people and businesses.

Similar articles
Comments

Bulletproof your development with remote team augmentation

Read how
This page is best viewed in portrait mode
Our websites and web services use cookies. We use cookies and collected data to enhance your experience, provide additional communication channels, improve marketing materials and enhance our offer. IRONIN SP. Z O.O. SP. K. is committed to protecting all the data that we collect or process in any way, especially data of personal nature. By accepting these terms you agree to our usage of cookies and processing your data, according to our Privacy Policy, and you declare that your browser settings reflect your preferences. Read more You have the right to revoke this agreement at any time, based on the terms of our Privacy Policy. You can change cookies settings in your browser. If you do not agree with us using cookies and processing your data, please change your cookies settings in your web browser and reject these terms. You can find more information about cookies, your data privacy This site uses cookies. By continuing to browse the site, you are agreeing to our use of cookies. data processing, and your rights in our Privacy Policy.