我的habtm
和Product
型号之间存在Category
关系。
我正在尝试编写一个搜索至少包含2个类别的产品的查询。
我使用以下代码:
p = Product.joins(:categories).group("product_id").having("count(product_id) > 1")
p.length # 178
当迭代它时,每次我调用product.categories
时,它都会对数据库进行新的调用 - 不好。 我想阻止这些来电并获得相同的结果。做更多的研究我已经看到我可以包含(includes
)我的categories
表,它会将所有表加载到内存中,因此当没有必要再次调用数据库时迭代。所以我使用以下代码:
p2 = Product.includes(:categories).joins(:categories).group("product_id").having("count(product_id) > 1")
p2.length # 178 - I compared and the objects are the same as last query
以下是我感到困惑的事情:
p.first.eql? p2.first # true
p.first.categories.eql? p2.first.categories # false
p.first.categories.length # 2
p2.first.categories.length # 1
为什么使用includes
查询我得到正确的对象,但我没有得到categories
关系?
答案 0 :(得分:2)
它与group
方法有关。您的p2
仅包含每种产品的第一个类别。
您可以将其分解为两个查询:
product_ids = Product.joins(:categories).group("product_id").having("count(product_id) > 1").pluck(:product_id)
result = Product.includes(:categories).find(product_ids)
是的,你打了几次数据库,但至少你在迭代时没有去数据库。
答案 1 :(得分:1)
您必须知道includes
与联接不起作用(joins
只会压制前者)。
另外当include
关联ActiveRecord
确定它是否会使用eager_load
(使用左连接)或preload
(使用单独的查询)时。包含只是其中之一的包装。
事情是preload
与联接有关!所以你可以这样做:
products = Product.preload(:categories). # this will trigger a separate query
joins(:categories). # this will build the relevant query
group("products.id").
having("count(product_id) > 1").
select("products.*")
请注意,这也会使数据库命中两次,但您不会有任何O(n)查询。