我有三个模型:Catalog
,Upload
和Product
。产品属于目录,上载属于产品。
我需要计算给定目录的所有产品的上传次数。
到目前为止,这是我一直在这样做的方式,对于大量上传或产品来说速度非常慢:
@products = Product.where(catalog_id: 123)
@uploads_count = Upload.where(product_id: @products.pluck(:id)).count
我想避免加载所有产品只是为了计数。 我应该使用原始SQL还是有更好的方法来使用ActiveRecord?
答案 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提供了一个很好的答案。