Rails:如何通过左连接表来减轻负载?

时间:2019-05-17 09:40:18

标签: ruby-on-rails activerecord

目前,我有一个控制器查询,可按如下方式获取产品和产品更新:

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):

2 个答案:

答案 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子句使用引用关联表(例如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在表上触发了左外部联接查询