Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Counting associated records in Rails

I am trying to get the number of comments from each of the posts in the database. The following, however:

Post.includes(:comments).group("posts.id").count("comments.id")

raises the mysql error "Unknown column comments.id", since the generated sql seems to completely ignore the includes():

SELECT COUNT(comments.id) AS count_comments_id, posts.id AS posts_id
FROM `posts` GROUP BY posts.id

Interestingly, replacing includes() with joins() will produce working sql:

Post.joins(:comments).group("posts.id").count("comments.id")

SELECT COUNT(comments.id) AS count_comments_id, posts.id AS posts_id
FROM `posts` INNER JOIN `comments` ON `comments`.`post_id` = `posts`.`id`
GROUP BY posts.id

but the above query excludes all posts with 0 comments, which is not what I want. What I do need is to produce the following SQL (but without writing SQL, he he he)

SELECT COUNT(comments.id) AS count_comments_id, posts.id AS posts_id
FROM `posts` LEFT OUTER JOIN `comments` ON `comments`.`post_id` = `posts`.`id`
GROUP BY posts.id
like image 966
Giuseppe Avatar asked Jun 18 '12 06:06

Giuseppe


3 Answers

The includes method will not do a join in all cases but rather batch-fetch the association for performance reasons (see Rails :include vs. :joins).

What you need to do is a joins and you where almost on the correct path but got the group clause a bit wrong:

Post.select("posts.*, COUNT(comments.id) as comment_count").joins("LEFT OUTER JOIN comments ON (comments.post_id = posts.id)").group("posts.id")

Note that this solution has the benefit or actually returning Post objects (using .count() returns a Hash on my Rails 3.2) so you can loop through actual post objects in your view and also access the property comment_count.

like image 123
Tigraine Avatar answered Nov 14 '22 09:11

Tigraine


This problem is actually way simpler nowadays:

Comment.joins(:post).group(:post_id).count(:post_id)

This will give you a map of {<post_id> => <count of comments>}

like image 34
Peter P. Avatar answered Nov 14 '22 08:11

Peter P.


Try this:

Post.select("COUNT(comments.id) AS count_comments_id, posts.id AS posts_id").
  includes(:comments).group("posts.id")
like image 2
Harish Shetty Avatar answered Nov 14 '22 08:11

Harish Shetty