Tools
Tools: Stop Killing Your Database: 5 ActiveRecord Tips for Faster Rails Apps
2026-02-22
0 views
admin
LEVEL 1: The Basics (Chaining) ## LEVEL 2: Cleaning up with Scopes ## LEVEL 3: Solving the N+1 Problem (Advanced) ## STEP 1: Use includes ## LEVEL 4: Pluck vs Map (Optimization) ## LEVEL 5: Use exists? instead of present? In this article, I want to share my journey with ActiveRecord. When I first started with Rails, I thought ActiveRecord was just "magic" to save data to the database. But after working on larger projects, I realized there is so much more to it. It is very easy to write code that works, but it is also very easy to write code that makes your app slow. Here are the levels of ActiveRecord knowledge, moving from simple queries to more advanced optimizations. Most beginners know find and where. But the cool thing about ActiveRecord is that you can chain these methods together endlessly until you actually ask for the data. For example, if you want to find users who are active and signed up recently: This generates only one SQL query. Rails is smart enough to wait until the last moment to touch the database. Very often I find myself repeating the same where conditions in multiple controllers.
For example: Post.where(published: true).where(deleted: false). It is annoying to type this every time. To fix this, we should use Scopes.
You define them in your model, and they act just like class methods. Now your controller code looks much cleaner: That's pretty much it. It makes your code look like plain English. This is the most common mistake I see in Rails apps. It happens when you load a parent record, and then loop through its children. Imagine you list 10 Posts, and for each post, you want to show the Author's name. This looks fine, right? Wrong.
Rails will run 1 query to get the Posts, and then 10 more queries (one for each post) to get the User. If you have 100 posts, that's 101 queries. This kills your app performance. To fix this, we need to tell ActiveRecord to fetch everything at once. Now Rails will only run 2 queries: It is a huge difference in speed. Always check your server logs. If you see many queries selecting from the same table over and over, you need includes. Sometimes you don't need the whole database object. You just need an array of IDs or Names.
A lot of developers do this: This instantiates a heavy ActiveRecord object for every user, just to grab one string. It consumes a lot of memory. This goes straight to the database and returns an array of strings. It ignores the model logic completely, which is much faster if you just need raw data. This is a small tip but very useful.
If you just want to check if a record exists, but you don't need to use the data, don't load the record. exists? returns a boolean and stops querying as soon as it finds one match. That's it for today. Mastering these few tricks will make your Rails applications much faster and your code much cleaner. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK:
# Instead of doing this
users = User.where(active: true)
recent_users = users.where('created_at > ?', 1.day.ago) # You can chain it nicely
User.where(active: true).where('created_at > ?', 1.day.ago) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Instead of doing this
users = User.where(active: true)
recent_users = users.where('created_at > ?', 1.day.ago) # You can chain it nicely
User.where(active: true).where('created_at > ?', 1.day.ago) COMMAND_BLOCK:
# Instead of doing this
users = User.where(active: true)
recent_users = users.where('created_at > ?', 1.day.ago) # You can chain it nicely
User.where(active: true).where('created_at > ?', 1.day.ago) COMMAND_BLOCK:
# post.rb
class Post < ApplicationRecord scope :live, -> { where(published: true, deleted: false) } scope :recent, -> { where('created_at > ?', 1.week.ago) }
end Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# post.rb
class Post < ApplicationRecord scope :live, -> { where(published: true, deleted: false) } scope :recent, -> { where('created_at > ?', 1.week.ago) }
end COMMAND_BLOCK:
# post.rb
class Post < ApplicationRecord scope :live, -> { where(published: true, deleted: false) } scope :recent, -> { where('created_at > ?', 1.week.ago) }
end COMMAND_BLOCK:
# posts_controller.rb
def index @posts = Post.live.recent
end Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# posts_controller.rb
def index @posts = Post.live.recent
end COMMAND_BLOCK:
# posts_controller.rb
def index @posts = Post.live.recent
end COMMAND_BLOCK:
# controller
@posts = Post.all # view
<% @posts.each do |post| %> <%= post.user.name %>
<% end %> Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# controller
@posts = Post.all # view
<% @posts.each do |post| %> <%= post.user.name %>
<% end %> COMMAND_BLOCK:
# controller
@posts = Post.all # view
<% @posts.each do |post| %> <%= post.user.name %>
<% end %> CODE_BLOCK:
@posts = Post.includes(:user).all Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
@posts = Post.includes(:user).all CODE_BLOCK:
@posts = Post.includes(:user).all COMMAND_BLOCK:
# This loads all User objects into memory (heavy)
User.all.map(&:email) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# This loads all User objects into memory (heavy)
User.all.map(&:email) COMMAND_BLOCK:
# This loads all User objects into memory (heavy)
User.all.map(&:email) COMMAND_BLOCK:
# This only fetches the specific column from DB (fast)
User.pluck(:email) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# This only fetches the specific column from DB (fast)
User.pluck(:email) COMMAND_BLOCK:
# This only fetches the specific column from DB (fast)
User.pluck(:email) COMMAND_BLOCK:
# Bad: Loads the post into memory just to check if it's there
if Post.where(id: params[:id]).present? ...
end # Good: Runs a smart SQL "LIMIT 1" query
if Post.exists?(params[:id]) ...
end Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Bad: Loads the post into memory just to check if it's there
if Post.where(id: params[:id]).present? ...
end # Good: Runs a smart SQL "LIMIT 1" query
if Post.exists?(params[:id]) ...
end COMMAND_BLOCK:
# Bad: Loads the post into memory just to check if it's there
if Post.where(id: params[:id]).present? ...
end # Good: Runs a smart SQL "LIMIT 1" query
if Post.exists?(params[:id]) ...
end - Fetch all Posts.
- Fetch all Users associated with those IDs.
how-totutorialguidedev.toaiserverssldatabase