如何计算具有一个或多个关联对象的记录数?

mar*_*ion 7 join ruby-on-rails associations ruby-on-rails-3.2

我有一个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....
Run Code Online (Sandbox Code Playgroud)

至:

> 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...
Run Code Online (Sandbox Code Playgroud)

到更高级的:

>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...
Run Code Online (Sandbox Code Playgroud)

和其他一些变体,它们都会产生类似的错误。

我究竟做错了什么?

注意:我想要的只是properties那有的数量photos.count > 0。我不需要所有属性和照片数量的哈希。换句话说,如果我的数据库中有5000个属性,则我想构建一个范围,使其仅返回实际包含照片的属性。

eng*_*nky 5

由于您想要的只是Property带有Photos的,因此您只需一个INNER JOIN。

Property.joins(:photos) 
Run Code Online (Sandbox Code Playgroud)

这就对了。如果你想要一个范围

class Property < ActiveRecord::Base
  scope :with_photos, -> {joins(:photos)} 
end 
Run Code Online (Sandbox Code Playgroud)

使用rails 3.2获取计数

Property.with_photos.count(distinct: true)  
Run Code Online (Sandbox Code Playgroud)

您还可以在rails 3.2中使用:

Property.count(joins: :photos, distinct: true) 
Run Code Online (Sandbox Code Playgroud)

ActiveRecord :: Calculations#count文档

这将执行

SELECT 
  COUNT(DISTINCT properties.id) 
FROM 
  properties
  INNER JOIN photos ON photos.property_id = properties.id
Run Code Online (Sandbox Code Playgroud)