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

And*_*rew 90 sql activerecord ruby-on-rails has-many

这似乎相当简单,但我不能让它出现在Google上.

如果我有:

class City < ActiveRecord::Base
  has_many :photos
end

class Photo < ActiveRecord::Base
  belongs_to :city
end
Run Code Online (Sandbox Code Playgroud)

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

City.where( photos.empty? )
Run Code Online (Sandbox Code Playgroud)

......但那不存在.那么,你如何做这种查询?


更新: 现在找到原始问题的答案,我很好奇,你如何构建逆?

IE:如果我想创建这些作为范围:

scope :without_photos, includes(:photos).where( :photos => {:city_id=>nil} )
scope :with_photos, ???
Run Code Online (Sandbox Code Playgroud)

And*_*rew 124

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

City.includes(:photos).where(photos: { city_id: nil })
Run Code Online (Sandbox Code Playgroud)

  • @ sixty4bit - 它的工作原理是因为当你执行`includes`时它会进行连接.在SQL连接中,除非更改查询的投影,否则将获得每行的两个表的所有字段(在本例中为城市和照片).因此,他正在利用它来检查是否存在所需的数据库标识符.如果不是,那么在加入的照片方面没有记录.你也可以使用`photos:{id:nil}`如果更清楚的话. (7认同)
  • 我不明白这是怎么回事?是不是在寻找没有"city_id"的照片?这与没有照片的城市不同,那个特定城市的id是外键. (4认同)

TeW*_*eWu 48

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

City.left_outer_joins(:photos).where(photos: {id: nil})
Run Code Online (Sandbox Code Playgroud)

这将导致SQL如下:

SELECT cities.*
FROM cities LEFT OUTER JOIN photos ON photos.city_id = city.id
WHERE photos.id IS NULL
Run Code Online (Sandbox Code Playgroud)

使用includes:

City.includes(:photos).where(photos: {id: nil})
Run Code Online (Sandbox Code Playgroud)

会有相同的结果,但会产生更加丑陋的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
Run Code Online (Sandbox Code Playgroud)


Yos*_*sho 23

尝试查找连接表中没有匹配记录的记录时,需要使用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')
Run Code Online (Sandbox Code Playgroud)

  • 如果有人想知道,`LEFT OUTER JOIN`相当于`LEFT JOIN` (3认同)

Oni*_*shi 6

我用了一个连接以获取所有的人照片:

scope :with_photos, -> { joins(:photos).distinct }

对于特定情况,更容易编写和理解.不过,我不确定连接和做包含的效率是多少