13
votes

So I have a Post and a User.
Post has_many users and a user belongs_to a post.
I need a find that will find all the Posts that dont have any users like the following:

Post.first.users
 => [] 
7

7 Answers

29
votes
Post.where("id not in (select post_id from users)")
21
votes

Learned this one just today:

Post.eager_load(:users).merge(User.where(id: nil))

Works with Rails 4+ at least.

Update:

In Rails 5+, you can use left_joins instead:

Post.left_joins(:users).merge(User.where(id: nil))
2
votes

something like that:

p = Post.arel_table
u = User.arel_table

posts = Post.find_by_sql(p.join(u).on(p[:user_id].eq(u[:p_id])).where(u[:id].eq(nil)).to_sql) 
2
votes

I know this is tagged as Rails 3, but if you are using Rails 4, I've been doing it like this.

Post.where.not(user_id: User.pluck(:id))
2
votes

If you need something that is fast, employ a SQL statement like:

SELECT * 
FROM posts p 
LEFT OUTER JOIN users u ON p.id = u.post_id 
WHERE u.id IS null
1
votes

i guess a sql with in can cause performance problems if database table has many rows. careful with that

1
votes

Post.first.users.empty? should be sufficient if users returns an array.

If you want to check for each post you could do

Post.each do |p|
  if p.users.empty?
    do whatever
  end
end