如何计算具有一个或多个关联对象的记录数?
How do I count the number of records that have one or more associated object?
我有一个 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 个属性,我想构建一个 returns 只是实际有照片的属性的范围。
编辑:
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 })
因为你正在使用 rails 3.2 .not
将无法工作你必须使用
Property.includes(:photos).where("property_id IS NOT null")
因为您只需要 Property
和 Photo
,那么您只需要一个 INNER JOIN。
Property.joins(:photos)
就是这样。如果你想要一个范围,那么
class Property < ActiveRecord::Base
scope :with_photos, -> {joins(:photos)}
end
使用 rails 3.2
获取计数
Property.with_photos.count(distinct: true)
您还可以使用:在 rails 3.2
Property.count(joins: :photos, distinct: true)
ActiveRecord::Calculations#count Doc
这将执行
SELECT
COUNT(DISTINCT properties.id)
FROM
properties
INNER JOIN photos ON photos.property_id = properties.id
试一试:
class Property < ApplicationRecord
has_many :photos
def self.with_photos
self.all.reject { |p| p.photos.empty? }
end
end
Property.with_photos.count
Source
更高效(Rails 4+):
Property.joins(:photos).uniq.count
Source
更高效(Rails 5.1+):
Property.joins(:photos).distinct.count
Source
根据您的要求,您可以试试这个
1) A simple count of the number of properties that have 1 or more
photo
要获得拥有一张或多张照片的房产数量,您可以这样做
Property.joins(:photos).distinct.count
由于我们不使用 group
,因此 distinct
或 uniq
是必需的。 distinct
将 return ActiveRecord_Relation
并且 uniq
将 return Array
.
2) I would like that set of properties returned so I can
create a scope of just those properties. Also, I do have lots of
properties with more than 1 photo.
要获取具有一张或多张照片的所有 属性 个对象,您可以使用相同的查询:
Property.joins(:photos).distinct
或者您可以使用 group_by
子句:
Property.joins(:photos).group('properties.id')
不同之处在于,当您在 group
查询中使用 size
方法时,它将 return 一个以 property_id 作为键和照片数量的散列在 属性 作为值。
性能更新:
如果您总是需要关联对象的计数并且想要高效地获取它,您可以像这样使用 counter_cache
:
class Photo < ApplicationRecord
belongs_to :property, counter_cache: true
end
class Property < ApplicationRecord
has_many :photos
end
然后您必须在属性 table 中添加一个名为 photos_count
的列,并且 Rails 将在每次添加新照片记录或现有照片记录时自动更新该列照片记录被删除。然后也可以直接查询:
Property.where('photos_count > ?', 1)
这是可选的,如果您遇到数据获取的性能问题,您可以这样做。
你可以这样试试,我在我的项目中做过,
Photo.group(:property_id).count
您将获得 属性 id 以及 照片数
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 }
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") }
我有一个 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 个属性,我想构建一个 returns 只是实际有照片的属性的范围。
编辑:
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 })
因为你正在使用 rails 3.2 .not
将无法工作你必须使用
Property.includes(:photos).where("property_id IS NOT null")
因为您只需要 Property
和 Photo
,那么您只需要一个 INNER JOIN。
Property.joins(:photos)
就是这样。如果你想要一个范围,那么
class Property < ActiveRecord::Base
scope :with_photos, -> {joins(:photos)}
end
使用 rails 3.2
获取计数Property.with_photos.count(distinct: true)
您还可以使用:在 rails 3.2
Property.count(joins: :photos, distinct: true)
ActiveRecord::Calculations#count Doc
这将执行
SELECT
COUNT(DISTINCT properties.id)
FROM
properties
INNER JOIN photos ON photos.property_id = properties.id
试一试:
class Property < ApplicationRecord
has_many :photos
def self.with_photos
self.all.reject { |p| p.photos.empty? }
end
end
Property.with_photos.count
Source
更高效(Rails 4+):
Property.joins(:photos).uniq.count
Source
更高效(Rails 5.1+):
Property.joins(:photos).distinct.count
Source
根据您的要求,您可以试试这个
1) A simple count of the number of properties that have 1 or more photo
要获得拥有一张或多张照片的房产数量,您可以这样做
Property.joins(:photos).distinct.count
由于我们不使用 group
,因此 distinct
或 uniq
是必需的。 distinct
将 return ActiveRecord_Relation
并且 uniq
将 return Array
.
2) I would like that set of properties returned so I can create a scope of just those properties. Also, I do have lots of properties with more than 1 photo.
要获取具有一张或多张照片的所有 属性 个对象,您可以使用相同的查询:
Property.joins(:photos).distinct
或者您可以使用 group_by
子句:
Property.joins(:photos).group('properties.id')
不同之处在于,当您在 group
查询中使用 size
方法时,它将 return 一个以 property_id 作为键和照片数量的散列在 属性 作为值。
性能更新:
如果您总是需要关联对象的计数并且想要高效地获取它,您可以像这样使用 counter_cache
:
class Photo < ApplicationRecord
belongs_to :property, counter_cache: true
end
class Property < ApplicationRecord
has_many :photos
end
然后您必须在属性 table 中添加一个名为 photos_count
的列,并且 Rails 将在每次添加新照片记录或现有照片记录时自动更新该列照片记录被删除。然后也可以直接查询:
Property.where('photos_count > ?', 1)
这是可选的,如果您遇到数据获取的性能问题,您可以这样做。
你可以这样试试,我在我的项目中做过,
Photo.group(:property_id).count
您将获得 属性 id 以及 照片数
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 }
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") }