Question

Using Rails 3, this scope works as would be expected:

      scope :with_posts, lambda {
        joins(:posts).
        select("authors.*, count(posts.id) posts_count").
        group("posts.author_id").
        having("posts_count > 0")
      }

The generated SQL is:

SELECT authors.*, count(posts.id) posts_count FROM `authors` INNER JOIN `posts` ON `posts`.`author_id` = `author`.`id` GROUP BY posts.author_id HAVING posts_count > 0

But its inverse returns no results:

      scope :with_posts, lambda {
        joins(:posts).
        select("authors.*, count(posts.id) posts_count").
        group("posts.author_id").
        having("posts_count < 1")
      }

I'm assuming that authors with zero posts are simply not being selected by line three... so what is the solution?

Was it helpful?

Solution

You are correct, the join is excluding all authors with zero posts, what you need is an outer join.

I don't know rails 3 syntax, but in rails 2, you can specify the joins statement with an SQL fragment instead of simply using the relation name.
User.all(:joins => 'outer join posts on users.id = posts.user_id')
Rails 3 must have an equivalent notation, but I never used it so I don't know the exact syntax. This should be the general idea though.

OTHER TIPS

Since inner joing SQL is being created it will show up the results with records available in both tables. So try creating outer join and find/count the null values in next table

try outer join

I don't know much about this but I would recommend you to have a look at this Railscast

Hope this works for you.

scope :without_posts, lambda {
    where("id NOT IN (?)", Posts.select("author_id").group("author_id") )
}

This will work best if you have an index on author_id in your DB.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top