2012-03-08 32 views
71

Điều này có vẻ khá đơn giản nhưng tôi không thể làm cho nó bật lên trên Google.Đường ray tìm bản ghi với 0 hồ sơ has_many liên quan

Nếu tôi có:

class City < ActiveRecord::Base 
    has_many :photos 
end 

class Photo < ActiveRecord::Base 
    belongs_to :city 
end 

Tôi muốn tìm tất cả các thành phố mà không có ảnh. Tôi rất muốn có thể gọi một cái gì đó như ...

City.where(photos.empty?) 

... nhưng điều đó không tồn tại. Vì vậy, làm thế nào để bạn làm loại truy vấn?


Cập nhật: Có bây giờ tìm thấy câu trả lời cho câu hỏi ban đầu, tôi tò mò, làm thế nào để bạn xây dựng nghịch đảo?

IE: nếu tôi muốn tạo ra những phạm vi hoạt động:

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

Kể từ khi tôi phát hiện ra câu hỏi này (http://stackoverflow.com/q/5319400/417872) Tôi đoán đây có thể được đóng . Nó có lẽ đáng giá để có thêm một cách để tìm thấy điều này trong google mặc dù, loại điều này là khó để mô tả và do đó khó tìm kiếm. – Andrew

+11

Trong Rails 4, bạn có thể sử dụng phương thức '.not' mới cho nghịch đảo. 'City.includes (: photos) .where.not (ảnh: {city_id: nil})' –

+2

Bản sao có thể có của [Muốn tìm các bản ghi không có bản ghi liên quan trong Rails 3] (https://stackoverflow.com/questions/ 5319400/muốn tìm-hồ sơ-với-không-liên kết-hồ sơ-trong-đường ray-3) –

Trả lời

103

Bah, tìm thấy nó ở đây: https://stackoverflow.com/a/5570221/417872

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

Xem thêm: http://stackoverflow.com/a/19080147/492465 - cũng trả lời câu hỏi của bạn về việc xây dựng nghịch đảo và làm tất cả điều này với Arel – novemberkilo

+4

Tôi không hiểu điều này là chính xác như thế nào? Không phải là nó tìm kiếm những bức ảnh không có 'city_id' sao?Đó không phải là thành phố mà không có ảnh nào với id của thành phố cụ thể đó là khóa ngoại. – sixty4bit

+4

@ sixty4bit - Nó hoạt động vì khi bạn làm 'include' nó tham gia. Trong một phép nối SQL, bạn nhận được tất cả các trường của cả hai bảng (trong trường hợp này là các thành phố và ảnh) cho mỗi hàng trừ khi bạn thay đổi phép chiếu của truy vấn. Vì vậy, anh ta sử dụng điều đó để lợi thế của mình để kiểm tra xem có một bộ nhận dạng cơ sở dữ liệu cần thiết có mặt hay không. Nếu không, thì không có hồ sơ nào ở phía bên ảnh. Bạn cũng có thể sử dụng 'ảnh: {id: nil}' nếu điều đó rõ ràng hơn. –

21

Khi cố gắng để tìm hồ sơ với không có hồ sơ phù hợp từ bảng tham gia, bạn cần sử dụng 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') 
+0

điều này là chậm hơn nhiều so với câu trả lời Andrew, nhưng nó hoạt động – brauliobo

+0

Thực ra tôi sẽ nói điều này là ít phức tạp - câu trả lời này thể hiện kỹ thuật thực tế được sử dụng, hoặc truy vấn DB thực tế sẽ là cần thiết. Câu trả lời được chấp nhận về cơ bản làm xáo trộn điều này. – Todd

+2

Nếu có ai khác tự hỏi, 'LEFT OUTER JOIN' tương đương với' LEFT JOIN' – Daniel

4

Tôi đã sử dụng kết nối để nhận tất cả những cái với ảnh:

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

dễ dàng hơn để viết và hiểu, cho rằng trường hợp cụ thể. Tôi không chắc chắn những gì hiệu quả là làm một tham gia vs làm một bao gồm, mặc dù

22

Trong Rails 5 để tìm tất cả các thành phố mà không có hình ảnh, bạn có thể sử dụng left_outer_joins:

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

mà sẽ cho kết quả trong SQL như:

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

Sử dụng includes:

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

sẽ có kết quả tương tự, nhưng sẽ cho kết quả trong SQL xấu xí giống như:

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

Phiên bản Rails 4 này là gì? – fatuhoku

+0

@fatuhoku Trong ** Rails 4 ** bạn có thể sử dụng [câu trả lời của Yossi] (https://stackoverflow.com/a/23756239/6231376) (cho SQL sạch hơn) hoặc sử dụng 'includes' (cho ít mã giòn). Trong Rails 5 'left_outer_joins' là cách. – TeWu

Các vấn đề liên quan