目前,我有一个控制器查询,可按如下方式获取产品和产品更新:
products = Product.left_outer_joins(:productupdates).select("products.*, count(productupdates.*) as update_count, max(productupdates.old_price) as highest_price").group(:id)
products = products.paginate(:page => params[:page], :per_page => 20)
此查询创建N + 1个查询,但由于也有一个遗漏的联接,因此我无法放置.include(:productsupdates)。
如果可以的话,您能帮我减少N + 1个查询吗?
编辑------------------------------ 根据维沙尔的建议;我已如下更改控制器查询,
products = product.includes(:productupdates).select("products.*, count(productupdates.*) as productupdate_count, max(productupdates.old_price) as highest_price").group("productupdates.product_id")
products = products.paginate(:page => params[:page], :per_page => 20)
很遗憾,我收到以下错误消息:
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: missing FROM-clause entry for table "productupdates"
LINE 1: SELECT products.*, count(productupdates.*) as productupdate_count, m...
^
: SELECT products.*, count(productupdates.*) as productupdate_count, max(productupdates.old_price) as highest_price FROM "products" WHERE "products"."isopen" = $1 AND (products.year > 2009) AND ("products"."make" IS NOT NULL) GROUP BY productupdates.product_id LIMIT $2 OFFSET $3):
答案 0 :(得分:2)
请告知这是如何导致N + 1的,以及您认为这将如何解决此问题。在这里看到N + 1情况的唯一方法是,如果以后要在每个产品上调用productupdate。如果是这种情况,那么这将无法解决问题。请告知,以便其他人可以制定适当的答复
暂时,我将假设您在代码的后面某处正在对各个产品调用productupdates
。如果是这种情况,那么我们可以在不进行汇总的情况下解决此问题
@products = Product.eager_load(:productupdates)
现在,当我们循环播放产品更新时,已经加载了它们,以便获得计数和最大值,我们可以执行
之类的操作@products.each do |p|
# COUNT
# (don't use the count method or it will execute a query )
p.productupdates.size
# MAX old_price
# older ruby versions use rails `try` instead
# e.g. p.productupdates.max_by(&:old_price).try(:old_price) || 0
p.productupdates.max_by(&:old_price)&.old_price || 0
end
使用这些方法将不会执行其他查询,因为产品更新已加载
旁注: includes
对您不起作用的原因是,includes
将使用2个查询来检索数据(sudo外连接),除非以下情况之一:满足条件:
where(productupdates: {old_price: 12})
)的哈希查找器条件references
方法(例如Product.includes(:productupdates).references(:productupdates)
)在这两种情况下,表都将保持连接状态。我选择在这种情况下使用紧急加载,因为在上述情况下,includes
委托给eager_load
答案 1 :(得分:-1)
您可以直接执行Product.includes(:productupdates)
,这将查询具有左外部联接的数据库,并且将克服N + 1查询问题。
因此,在查询中不要使用Product.left_outer_joins(:productupdates)
Product.includes(:productupdates)
在控制台中触发此查询后,您可以看到includes
在表上触发了左外部联接查询