views:

1595

answers:

2

What is the "rails way" to efficiently grab all rows of a parent table along with a count of the number of children each row has?

I don't want to use counter_cache as I want to run these counts based on some time conditions.

The cliche blog example: Table of articles. Each article has 0 or more comments.

I want to be able to pull how many comments each article has in the past hour, day, week.

However, ideally I don't want to iterate over the list and make seperate sql calls for each article nor do I want to use :include to prefetch all of the data and process it on the app server.

I want to run one SQL statement and get one result set with all the info.

I know I can hard code out the full SQL, and maybe could use a .find and just set the :joins, :group, and :conditions parameters... BUT I am wondering if there is a "better" way... aka "the rails way"

Thanks in advance

+2  A: 

From a SQL perspective, this looks trivial - Just write up a new query.

From a Rails perspective, The values you mention are computed values. So if you use find_by_sql, the Model class would not know about the computed fields and hence would return the computed values as strings even if you manage to translate the query into Rails speak. See linked question below.
The general drift (from the responses I got to that question) was to have a separate class be responsible for the rollup / computing the desired values.

http://stackoverflow.com/questions/659523/how-to-get-rails-to-return-sumcolumnname-attributes-with-right-datatype-instead

Gishu
+8  A: 

This activerecord call should do what you want:

Article.find(:all, :select => 'articles.*, count(posts.id) as post_count',:joins => 'left outer join posts on posts.article_id = articles.id', :group => 'articles.id')

This will return a list of article objects, each of which has the method "post_count" on it that contains the number of posts on the article as a string.

The method executes sql similar to the following:

SELECT articles.*, count(posts.id) as post_count FROM `articles` left outer join posts on posts.article_id = articles.id GROUP BY articles.id

If you're curious, this is a sample of the mysql results you might see from running such a query:

+----+----------------+------------+
| id | text           | post_count |
+----+----------------+------------+
|  1 | TEXT TEXT TEXT |          1 |
|  2 | TEXT TEXT TEXT |          3 |
|  3 | TEXT TEXT TEXT |          0 |
+----+----------------+------------+
Gdeglin