2012-03-08 62 views
71

这似乎相当简单,但我无法让它在Google上出现。Rails找到零记录has_many记录相关联

如果我有:

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, ??? 
+4

因为我发现了这个问题(http://stackoverflow.com/q/5319400/417872)我想这可以被关闭。可能还有一种方法可以在谷歌上找到这种方法,这种情况很难描述,因此很难搜索。 – Andrew 2012-03-08 06:33:20

+11

在Rails 4中,您可以使用新的'.not'方法进行反转。 'City.includes(:photos).where.not(photos:{city_id:nil})' – 2014-12-01 22:17:12

+2

[想要在Rails 3中查找没有关联记录的记录](https://stackoverflow.com/questions/ 5319400 /想要找到记录与无关联记录在轨-3) – 2018-01-24 09:30:57

回答

103

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

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

另请参阅:http://stackoverflow.com/a/19080147/492465 - 也解答了你关于构造逆的问题并用Arel做这一切 – novemberkilo 2013-09-29 16:06:55

+4

我不明白这是怎么回事?是不是在寻找没有“city_id”的照片?这与那些没有照片的城市不同,该城市的ID是外键。 – sixty4bit 2015-07-02 01:33:43

+4

@ sixty4bit - 它的工作原理是因为当你做'包括'它做了一个连接。在SQL连接中,除非更改查询的投影,否则可以获取每行的两个表(在本例中为城市和照片)的所有字段。因此,他正在利用这一点来检查是否存在所需的数据库标识符。如果不是,那么在连接的照片一侧没有记录。如果更清楚,你也可以使用'照片:{id:nil}'。 – 2015-12-08 18:28:09

21

当试图找到从连接表中没有匹配记录的记录,你需要使用一个左外连接

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') 
+0

这是比安德鲁答案慢得多,但它的工作 – brauliobo 2015-10-30 19:48:26

+0

其实我会说这不复杂 - 这个答案演示了实际使用的技术,或实际的数据库查询将需要。接受的答案基本上混淆了这一点。 – Todd 2016-03-23 14:55:20

+2

如果有其他人想知道,'LEFT OUTER JOIN'相当于'LEFT JOIN' – Daniel 2016-05-01 03:37:01

4

我用一个连接来获得所有的照片:

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

更容易编写和理解,对于具体情况。我不知道效率做一个加入VS做一个包括什么,但

22

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 
+0

这是什么Rails 4版本? – fatuhoku 2017-05-19 14:47:05

+0

@fatuhoku在** Rails 4 **中,您可以使用[Yossi's answer](https://stackoverflow.com/a/23756239/6231376)(适用于更简洁的SQL)或使用'includes'(适用于较脆弱的代码)。在Rails 5中''left_outer_joins'就是这样。 – TeWu 2017-05-23 17:08:03

相关问题