Rails находит запись с нулевыми записями has_many - PullRequest
87 голосов
/ 08 марта 2012

Это кажется довольно простым, но я не могу включить его в 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 ]

121 голосов
/ 08 марта 2012

Бах, нашел его здесь: https://stackoverflow.com/a/5570221/417872

City.includes(:photos).where(photos: { city_id: nil })
45 голосов
/ 09 сентября 2016

В Рельсы 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
22 голосов
/ 20 мая 2014

При попытке найти записи без соответствующих записей в объединенной таблице необходимо использовать 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')
6 голосов
/ 30 апреля 2015

Я использовал объединение, чтобы получить все с фотографиями:

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

Проще написать и понять, для этого конкретного случая. Я не уверен, насколько эффективны объединения, а не включения, хотя

0 голосов
/ 27 июня 2019

Я не верю, что принятый ответ даст вам именно то, что вы ищете, так как вы хотите сделать 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) )
...