A lot of times when writing rails apps, you face a situation where you have to count the number of child objects a parent object has.
For instance, let’s say we have a
User model and a
Comment model where
has_many :comments and
What do we do when we want to count the number of comments that a
User has? A lot of times, it would look something like this:
This is all fine, if you’re only using it once in a while.
But say we need to query the database often for the number of comments that a
Let’s see what the performance is like when using
In an example app I created with the same models as above, I tested this out with a
user with 225
Notice how the
comments are queried with a COUNT(*) query. If we were to query 100 users at once for
comments.count, it would be a big load for the database.
Here’s how we can avoid the COUNT(*) query and improve performance:
Instead of querying
comments, we can just store the number of comments a user has in the
User table and get the number of comments without any SQL query.
In order for
counter_cache to work, we need to create a database column that ends with
_count. So here, we have created a
comments_count column that will store the number of comments inside.
After we run the migration, we want to initially update the
comments_count for each
We can do this by creating a rake file, or manually running code in the console:
reset_column_information resets all the cached information about columns, and will be reloaded before the code below runs.
Now after we run this code, we see that the correct number of comments are stored in the user object:
One more thing we need to do:
Comment model, we need to set
This will increment and decrement the
comments_count column automatically in
User whenever a comment is created or deleted.
Let’s test this out:
Awesome! As you can see, it properly incremented the
counter_cache can increase performance when you are trying to display, for instance, many users and their comment counts. Instead of making a bunch of COUNT(*) queries, by using
counter_cache, you can access the same information by just accessing a column in the