Как подсчитать количество записей, имеющих один или несколько связанных объектов? - PullRequest
0 голосов
/ 17 января 2019

У меня есть Property модель, которая has_many :photos. Я хочу посчитать количество properties, у которых есть одна или несколько фотографий.

Как мне это сделать?

Я попробовал простое:

> Property.where('properties.photos.count > ?', 0).count

   (3.1ms)  SELECT COUNT(*) FROM "properties" WHERE (properties.photos.count > 1)
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "photos"
LINE 1: SELECT COUNT(*) FROM "properties"  WHERE (properties.photos....
                                                  ^
: SELECT COUNT(*) FROM "properties"  WHERE (properties.photos.count > 0)
from /ruby-2.3.0@myproject/gems/activerecord-3.2.22.5/lib/active_record/connection_adapters/postgresql_adapter.rb:1163:in `async_exec'
Caused by PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "photos"
LINE 1: SELECT COUNT(*) FROM "properties"  WHERE (properties.photos....

до:

> Property.joins(:photos).where('photos.count > ?', 0).count

   (3.7ms)  SELECT COUNT(*) FROM "properties" INNER JOIN "photos" ON "photos"."property_id" = "properties"."id" WHERE (photos.count > 0)
ActiveRecord::StatementInvalid: PG::GroupingError: ERROR:  aggregate functions are not allowed in WHERE
LINE 1: ..."photos"."property_id" = "properties"."id" WHERE (photos.cou...
                                                             ^
: SELECT COUNT(*) FROM "properties" INNER JOIN "photos" ON "photos"."property_id" = "properties"."id" WHERE (photos.count > 0)
from ruby-2.3.0@myproject/gems/activerecord-3.2.22.5/lib/active_record/connection_adapters/postgresql_adapter.rb:1163:in `async_exec'
Caused by PG::GroupingError: ERROR:  aggregate functions are not allowed in WHERE
LINE 1: ..."photos"."property_id" = "properties"."id" WHERE (photos.cou...

для более продвинутых:

>Property.includes(:photos).group(['property.id', 'photos.id']).order('COUNT(photos.id) DESC').count

(0.6ms)  SELECT COUNT(DISTINCT "properties"."id") AS count_id, property.id AS property_id, photos.id AS photos_id FROM "properties" LEFT OUTER JOIN "photos" ON "photos"."property_id" = "properties"."id" GROUP BY property.id, photos.id ORDER BY COUNT(photos.id) DESC
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "property"
LINE 1: ...CT COUNT(DISTINCT "properties"."id") AS count_id, property.i...
                                                             ^
: SELECT COUNT(DISTINCT "properties"."id") AS count_id, property.id AS property_id, photos.id AS photos_id FROM "properties" LEFT OUTER JOIN "photos" ON "photos"."property_id" = "properties"."id" GROUP BY property.id, photos.id ORDER BY COUNT(photos.id) DESC
from ruby-2.3.0@myproject/gems/activerecord-3.2.22.5/lib/active_record/connection_adapters/postgresql_adapter.rb:1163:in `async_exec'
Caused by PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "property"
LINE 1: ...CT COUNT(DISTINCT "properties"."id") AS count_id, property.i...

и несколько других вариантов, и все они выдают схожие ошибки.

Что я делаю не так?

Примечание: все, что я хочу, это счет properties, который имеет photos.count > 0. Я не хочу хэш всех свойств и количества фотографий. Другими словами, если в моей базе данных есть 5000 свойств, я хочу построить область видимости, которая возвращает только те свойства, которые действительно имеют фотографии.

Ответы [ 6 ]

0 голосов
/ 02 июня 2019
Property.includes(:photos).where("SELECT count(photos.id) > 0 FROM photos WHERE property_id = properties.id")

Как сфера:

scope :with_photos, -> { where("SELECT count(photos.id) > 0 FROM photos WHERE property_id = properties.id") }
0 голосов
/ 01 июня 2019

Вы можете попробовать вот так, я делал в своих проектах,

Photo.group(:property_id).count

Вы получите идентификатор недвижимости с количеством фотографий

results = { 3314=>3, 2033=>3, 3532=>2, 3565=>6, 3510=>1, 3022=>7, 648=>2, 570=>3, 4678=>3, 3540=>1, 3489=>4, 536=>1, 1715=>4 }
0 голосов
/ 01 июня 2019

По вашему требованию вы можете попробовать это

1) Простой подсчет количества свойств, которые имеют 1 или более фото

Чтобы получить количество объектов, на которых есть одна или несколько фотографий, вы можете сделать это

Property.joins(:photos).distinct.count

Поскольку мы не используем group, distinct или uniq необходимы. distinct вернет ActiveRecord_Relation и uniq вернет Array.

2) Я хотел бы, чтобы этот набор свойств был возвращен, чтобы я мог создать область только этих свойств. Кроме того, у меня есть много свойства с более чем 1 фото.

Чтобы получить все объекты недвижимости, которые имеют одну или несколько фотографий, вы можете использовать один и тот же запрос:

Property.joins(:photos).distinct

или вы можете использовать предложение group_by:

Property.joins(:photos).group('properties.id')

Разница будет в том, что когда вы будете использовать метод size в запросе group, он вернет хеш с property_id в качестве ключа и количество фотографий в свойстве в качестве значения.

Обновление производительности:

Если вам всегда требуется количество связанных объектов и вы хотите эффективно их извлекать, вы можете использовать counter_cache следующим образом:

class Photo < ApplicationRecord
  belongs_to :property, counter_cache: true
end

class Property < ApplicationRecord
  has_many :photos
end

И затем вам нужно будет добавить столбец в таблицу свойств с именем photos_count, и Rails автоматически обновит столбец при каждом добавлении новой записи фотографии или удалении существующей записи фотографии. И тогда вы также можете запросить напрямую:

Property.where('photos_count > ?', 1)

Это необязательно, и вы можете сделать это, если у вас возникают проблемы с производительностью при получении данных.

0 голосов
/ 29 мая 2019

Попробуй:

class Property < ApplicationRecord
  has_many :photos

  def self.with_photos
    self.all.reject { |p| p.photos.empty? }
  end
end

Property.with_photos.count

Источник

Более эффективный (Rails 4 +):

Property.joins(:photos).uniq.count

Источник

Более эффективный (Rails 5.1 +):

Property.joins(:photos).distinct.count

Источник

0 голосов
/ 28 мая 2019

Поскольку все, что вам нужно, это Property с Photo с, то ВНУТРЕННЕЕ СОЕДИНЕНИЕ - это все, что вам нужно.

Property.joins(:photos) 

Вот и все. Если вы хотите прицел, то

class Property < ActiveRecord::Base
  scope :with_photos, -> {joins(:photos)} 
end 

Для подсчета используйте рельсы 3,2

Property.with_photos.count(distinct: true)  

Вы также можете использовать: в рельсах 3,2

Property.count(joins: :photos, distinct: true) 

ActiveRecord :: Расчеты # count Doc

Это выполнит

SELECT 
  COUNT(DISTINCT properties.id) 
FROM 
  properties
  INNER JOIN photos ON photos.property_id = properties.id
0 голосов
/ 17 января 2019

EDIT:

Property.joins(:photos).group('photos.property_id').having('count(photos.property_id) > 1').count

#=> {1234=>2}  # 1234 is property id 2 is count of photos 

Вы получите property_ids с количеством связанных с ним фотографий.

Старый ответ:

Вы можете получить свойства по крайней мере с одной фотографией, связанной с ним

Property.includes(:photos).where.not(photos: { property_id: nil })

Поскольку вы используете рельсы 3.2 .not не будет работать, вы должны использовать

Property.includes(:photos).where("property_id IS NOT null")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...