Ruby on rails 包括/连接rails 4中的机箱
我的Ruby on rails 包括/连接rails 4中的机箱,ruby-on-rails,ruby-on-rails-4,activerecord,Ruby On Rails,Ruby On Rails 4,Activerecord,我的产品和类别模型之间存在habtm关系。 我正在尝试编写一个查询,搜索至少有两个类别的产品 我用以下代码实现了它: p = Product.joins(:categories).group("product_id").having("count(product_id) > 1") p.length # 178 p2 = Product.includes(:categories).joins(:categories).group("product_id").having("count(p
产品
和类别
模型之间存在habtm
关系。
我正在尝试编写一个查询,搜索至少有两个类别的产品
我用以下代码实现了它:
p = Product.joins(:categories).group("product_id").having("count(product_id) > 1")
p.length # 178
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
但是,在对它进行迭代时,每次我调用product.categories
,它都会对数据库进行新的调用-这不太好我希望阻止这些调用并获得相同的结果。做了更多的研究后,我发现我可以将(includes
)包含在我的categories
表中,它会将所有表加载到内存中,因此在迭代时不必再次调用数据库。因此,我使用以下代码实现了它:
p = Product.joins(:categories).group("product_id").having("count(product_id) > 1")
p.length # 178
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
查询,我得到了正确的对象,但没有得到正确的类别
关系?这与组
方法有关。您的p2
仅包含每个产品的第一个类别
您可以将其分为两个查询:
product_ids = Product.joins(:categories).group("product_id").having("count(product_id) > 1").pluck(:product_id)
result = Product.includes(:categories).find(product_ids)
是的,你点击了数据库两次,但至少你在迭代时不去数据库。这与
组
方法有关。您的p2
仅包含每个产品的第一个类别
您可以将其分为两个查询:
product_ids = Product.joins(:categories).group("product_id").having("count(product_id) > 1").pluck(:product_id)
result = Product.includes(:categories).find(product_ids)
是的,你点击了数据库两次,但至少你在迭代时没有进入数据库。你必须知道,
包含
不能很好地处理连接(连接
只会抑制前者)
另外,当您包含
时,关联ActiveRecord
会确定它是使用eager\u load
(使用左连接)还是预加载
(使用单独的查询)。Includes只是其中一个的包装器
问题是,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)查询。您必须知道,
includes
不能很好地处理联接(joins
只会抑制前者)
另外,当您包含
时,关联ActiveRecord
会确定它是使用eager\u load
(使用左连接)还是预加载
(使用单独的查询)。Includes只是其中一个的包装器
问题是,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)查询。这就像一个符咒。非常感谢@Jason,他工作得很有魅力。非常感谢@JasonLove您的解决方案和解释@charlysisto。Love您的解决方案和解释@charlysisto。