ActiveRecord查询不拥有汽车的用户

时间:2014-03-31 22:53:20

标签: ruby-on-rails ruby-on-rails-3 activerecord ruby-on-rails-4 rails-activerecord

如何让所有的用户拥有汽车?

class User < ActiveRecord::Base
  has_one :car
end

class Car < ActiveRecord::Base
  belongs_to :user
end

我正在做以下事情:

all.select {|user| not user.car }

直到我的用户和汽车数据库变得太大,现在我得到了奇怪的错误,特别是当我尝试对结果进行排序时,这种情况很有效。我需要在查询和排序以及查询的一部分中进行过滤。

更新 :我做的是以下内容:

where('id not in (?)', Car.pluck(:user_id)).order('first_name, last_name, middle_name')

它很慢,因为Rails必须从user_id表中获取所有cars,然后发出一个巨大的查询。我知道我可以在SQL中进行子查询,但必须有更好的Rails / ActiveRecord方式。

更新2 :我现在的查询效率明显提升:

includes(:car).where(cars: {id: nil})

我在下面接受的答案是joins,其中包含SQL字符串而不是includes。我不知道includes是否效率更低,因为它将nil数据存储在Ruby对象中,而joins可能不存在?我喜欢不使用字符串......

2 个答案:

答案 0 :(得分:0)

一种方法是使用从users表到cars表的左连接,并仅获取cars表中没有任何相应值的用户条目,看起来像:

User.select('users.*').joins('LEFT JOIN cars ON users.id = cars.user_id').where('cars.id IS NULL')

答案 1 :(得分:0)

这里需要完成的大部分工作都是SQL。试试这个:

User.joins("LEFT OUTER JOIN cars ON users.id = cars.user_id").where("cars.id IS NULL")

使用ruby执行此操作非常低效,正如您似乎正在尝试的那样。

您也可以在那里下订单:

User.
  joins("LEFT OUTER JOIN cars ON users.id = cars.user_id").
  where("cars.id IS NULL").
  order(:first_name, :last_name, :middle_name)

您可以将此作为User模型的范围,这样您只有一个地方可以处理它:

class User < ActiveRecord::Base
  has_one :car

  def self.without_cars
    joins("LEFT OUTER JOIN cars ON users.id = cars.user_id").
    where("cars.id IS NULL").
    order(:first_name, :last_name, :middle_name)
  end
end

这样你就可以:

User.without_cars

在您的控制器或其他方法中,甚至链接范围:

User.without_cars.where("users.birthday > ?", 18.years.ago)

找到没有18岁以下汽车的用户(任意例子,但你明白了)。我的观点是,这种事情应该总是变成一个范围,所以它可以与其他范围链接:) Arel这样很棒。