2012-03-08 115 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