Joins vs Includes in Rails: Key Differences and When to Use Each
joins performs an SQL INNER JOIN to combine tables and filter results at the database level, while includes uses eager loading to preload associated records and avoid N+1 queries. Use joins when you want to filter or sort by associated table columns, and includes when you want to load associations efficiently without filtering.Quick Comparison
This table summarizes the main differences between joins and includes in Rails.
| Factor | joins | includes |
|---|---|---|
| Purpose | Combine tables for filtering or sorting | Eager load associations to avoid N+1 queries |
| SQL Behavior | Generates INNER JOIN SQL | Generates separate queries or LEFT OUTER JOIN |
| When to Use | Filter or sort by associated table columns | Load associated records for later use |
| Performance | Single query with join, faster filtering | Multiple queries or LEFT OUTER JOIN, reduces N+1 queries |
| Result Type | Returns records matching join conditions | Returns all main records with preloaded associations |
Key Differences
joins creates an SQL INNER JOIN between tables, which means it only returns records that have matching associated records. This is useful when you want to filter or sort based on columns in the associated table. For example, finding users who have posts with a certain title.
On the other hand, includes is designed to eager load associations to avoid the N+1 query problem. It loads the main records and their associated records in separate queries or sometimes with a LEFT OUTER JOIN, so all main records are returned regardless of whether they have associated records.
While joins focuses on filtering and combining tables in one query, includes focuses on loading associated data efficiently for later use in your code without extra queries.
Code Comparison
Example: Find users who have posts with the title 'Hello'.
User.joins(:posts).where(posts: { title: 'Hello' })Includes Equivalent
Example: Load users and their posts to avoid N+1 queries when accessing posts.
users = User.includes(:posts).where(name: 'Alice') users.each do |user| puts user.posts.map(&:title) end
When to Use Which
Choose joins when you need to filter, sort, or query based on columns in associated tables because it performs an INNER JOIN and returns only matching records.
Choose includes when you want to load associated records upfront to avoid multiple database queries (N+1 problem), especially when you will access those associations later in your code.
Using includes without filtering on associated tables is best for performance when you just need the related data loaded.
Key Takeaways
joins performs INNER JOIN for filtering and sorting by associated table columns.includes eager loads associations to avoid N+1 queries by loading related records upfront.joins when filtering on associated data; use includes when just loading associations.joins returns only records with matching associations; includes returns all main records with preloaded associations.