Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails User.joins.not(...) in Active Record?

Im looking to query all Users without Comments in a single sql query?

Models:

class User < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :user
end

So I want the opposite of this:

User.joins.(:comments).group('users.id')

But not like this: (because it generates two queries)

User.where.not(id: Comment.pluck(:user_id))

Maybe something like this?

User.joins.not.(:comments).group('users.id')

Thanks for any input!

like image 982
James Avatar asked Apr 16 '14 02:04

James


People also ask

What is the difference between joins and includes in Rails?

The main difference is that when using includes the eager loading is used. Eager loading allows you to optimize data retrieval by avoiding additional queries to the database.

What is ActiveRecord in Ruby on Rails?

What is ActiveRecord? ActiveRecord is an ORM. It's a layer of Ruby code that runs between your database and your logic code. When you need to make changes to the database, you'll write Ruby code, and then run "migrations" which makes the actual changes to the database.

What is ActiveRecord relation in Rails?

The Relation Class. Having queries return an ActiveRecord::Relation object allows us to chain queries together and this Relation class is at the heart of the new query syntax. Let's take a look at this class by searching through the ActiveRecord source code for a file called relation.

What is eager loading Rails?

Eager loading is a way to find objects of a certain class and a number of named associations. Here I share my thoughts on using it with Rails. What are N + 1 queries? It mainly occurs when you load the bunch of objects and then for each object you make one more query to find associated object.


1 Answers

You can accomplish this with:

User.includes(:comments).where.not(comments: { id: nil })

This will result in raw SQL that looks something like:

SELECT DISTINCT `users`.`*` FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `comments`.`id` IS NULL

For accomplishing this via a subquery, see the below answer.

Old Answer:

You can do something like

User.where.not(id: Comment.select(:user_id))

If you wanted a single (though nested) query.

Otherwise, check out http://guides.rubyonrails.org/active_record_querying.html#joining-tables for using an outer join.

like image 90
Darren Cheng Avatar answered Sep 17 '22 15:09

Darren Cheng