Rails找到关联的零has_many记录的记录

时间:2012-03-08 06:25:25

标签: sql ruby-on-rails activerecord has-many

这似乎相当简单,但我无法让它出现在Google上。

如果我有:

class City < ActiveRecord::Base
  has_many :photos
end

class Photo < ActiveRecord::Base
  belongs_to :city
end

我想找到所有没有照片的城市。我很乐意打电话给... ...

City.where( photos.empty? )

......但那不存在。那么,你如何进行这种查询呢?


更新 现在已经找到了原始问题的答案,我很好奇,你如何构建反向?

IE:如果我想将它们作为范围创建:

scope :without_photos, includes(:photos).where( :photos => {:city_id=>nil} )
scope :with_photos, ???

5 个答案:

答案 0 :(得分:116)

Bah,在这里找到:https://stackoverflow.com/a/5570221/417872

City.includes(:photos).where(photos: { city_id: nil })

答案 1 :(得分:41)

Rails 5 中,要找到所有没有照片的城市,可以使用left_outer_joins

City.left_outer_joins(:photos).where(photos: {id: nil})

将导致SQL如:

SELECT cities.*
FROM cities LEFT OUTER JOIN photos ON photos.city_id = city.id
WHERE photos.id IS NULL

使用includes

City.includes(:photos).where(photos: {id: nil})

将具有相同的结果,但会产生更加丑陋的SQL,如:

SELECT cities.id AS t0_r0, cities.attr1 AS t0_r1, cities.attr2 AS t0_r2, cities.created_at AS t0_r3, cities.updated_at AS t0_r4, photos.id AS t1_r0, photos.city_id AS t1_r1, photos.attr1 AS t1_r2, photos.attr2 AS t1_r3, photos.created_at AS t1_r4, photos.updated_at AS t1_r5
FROM cities LEFT OUTER JOIN photos ON photos.city_id = cities.id
WHERE photos.id IS NULL

答案 2 :(得分:22)

尝试查找连接表中没有匹配记录的记录时,需要使用LEFT OUTER JOIN

scope :with_photos, joins('LEFT OUTER JOIN photos ON cities.id = photos.city_id').group('cities.id').having('count(photos.id) > 0')
scope :without_photos, joins('LEFT OUTER JOIN photos ON cities.id = photos.city_id').group('cities.id').having('count(photos.id) = 0')

答案 3 :(得分:0)

我不相信接受的答案会为您提供您想要的东西,就像您想做一个LEFT OUTER JOIN,而这个答案会给您一个INNER JOIN。至少在Rails 5中,您可以使用:

scope :without_photos, left_joins(:photos).where( photos: {id: nil} )

或者在命名空间会使merge子句变得笨拙的情况下,可以使用where

scope :without_photos, left_joins(:photos).merge( Photos.where(id: nil) )

答案 4 :(得分:0)

如果您没有运行Rails 5+,并且性能是必不可少的,请避免创建无用的ActiveRecord并获得所需的内容:

City.where("NOT EXISTS(SELECT 1 FROM photos WHERE photos.city_id = cities.id LIMIT 1)")