Rails ActiveRecord is a quite useful piece of software, though it lacks some features and way of achieving the desired result is not always obvious.

Let’s assume that we have two tables in our database: categories and posts. We would like to get most essential categories; i.e. those having at least 10 posts assigned to them. We can achieve this using the SQL JOIN, GROUP BY and HAVING clauses. However, there is no :having parameter on the find() method’s parameter list.

Writing the whole SQL query is one of the workarounds, but there is a better one. We can simply add the HAVING clause to the :group parameter, like in the following find() invocation:

def self.most_essential_categories
  find(:all, :select => 'categories.name, COUNT(posts.id) AS posts_count',
             :joins  => 'INNER JOIN posts ON posts.category_id = categories.id',
             :group  => 'categories.id, categories.name HAVING COUNT(posts.id) >= 10')
end

That’s it, the most_essential_categories() method returns names and posts count of categories having at least 10 posts assigned to them. INNER JOIN ensures that only categories having at least one post are being taken into account. By the way, the INNER keyword can be omitted due to the HAVING COUNT(posts.id) >= 10 condition and because INNER is the default JOIN type.

Note that the trick we just introduced can be used together with other SQL aggregate functions, like MIN(), MAX() or AVG(), not only the COUNT() function.

comments powered by Disqus