在where子句之后计算记录

时间:2018-06-13 08:05:10

标签: ruby-on-rails activerecord

我有三个模型:CatalogUploadProduct。产品属于目录,上载属于产品。

我需要计算给定目录的所有产品的上传次数。

到目前为止,这是我一直在这样做的方式,对于大量上传或产品来说速度非常慢:

@products = Product.where(catalog_id: 123)
@uploads_count = Upload.where(product_id: @products.pluck(:id)).count

我想避免加载所有产品只是为了计数。 我应该使用原始SQL还是有更好的方法来使用ActiveRecord?

2 个答案:

答案 0 :(得分:2)

这应该适合你:

Upload.joins(:product).where(products: { catalog_id: 123 }).count

使用joins在两个表之间创建INNER JOIN,允许您按上述方式查询products表。

请注意product的单数和复数用法 - 连接应反映关联(upload属于一个 product),而where子句始终使用表名,通常是复数。

SQL看起来类似于:

SELECT "uploads".* FROM "uploads" 
INNER JOIN "products" 
ON "products"."id" = "uploads"."product_id" 
WHERE "products"."catalog_id" = 123

如果您需要有关于目录的更多信息,您还可以包含以下内容:

Upload.joins(product: :catalog).where(products: { catalogs: { whatever: 'you want to query' } }).count

请记住,使用joins仅适用于此类查询。如果需要访问产品或目录的属性,则应使用其他方法(例如includes)来预加载数据并避免N + 1查询。如果您有兴趣,可以阅读here

答案 1 :(得分:1)

避免选择记录的另一种方法是使用子查询。这可以通过以下方式完成:

query = User.where(id: 1..100)
User.where(id: query.select(:id)).count
# [DEBUG]  (10.5ms)  SELECT COUNT(*) FROM "users" WHERE "users"."id" IN (SELECT "users"."id" FROM "users" WHERE ("users"."id" BETWEEN $1 AND $2))  [["id", 1], ["id", 100]]
# => 33

因此,User.where(id: 1..100)准备一个查询,可以用作子选择。 .select(:field)告诉您感兴趣的字段。 虽然对于基本计数,SRack提供了一个很好的答案。