【问题标题】:Eager Loading Optimization in ruby on railsruby on rails 中的急切加载优化
【发布时间】:2015-08-05 10:20:45
【问题描述】:

我有一个产品有_many Variants。一个变体属于一个产品。我想显示产品的名称(可以在 Product 中找到)以及价格和数量(可以在 Variants 中找到)。

Product table:
 -id
 -title
 -description

Variants table:
 - id
 - is_active(boolean)
 - price
 - quantity
 - product_id

这就是表格的样子。这是我的尝试

  def index

    @products =Product.all
    @display = []
    @products.each do |product|
      @children = product.variants.where(is_active: true).order(:price).
                                   select(:id,:price,:quantity).first

      if @children.present?
      @display << {
                   id: product.id,
                    title: product.title,
                    description: product.description,
                    price:  @children.price,
                    quantity:  @children.quantity,
                    variant_id:  @children.id
                  }
      end
    end

    @display = Kaminari.paginate_array(@display).page(params[:page])

  end

我需要最大限度地优化它。这让我想到了我的第一个问题。我怎样才能更好地优化它。
还有我的第二个问题,为什么当我执行 @products = Product.all.includes(:variants) 时,它实际上会增加加载时间而不是降低加载时​​间,因为我确实在整个 @products 数组中获得了该迭代中每个产品的变体(所以它应该算作 N+1,我为每个产品获得产品,我得到变体)? 将我的@products 数组拆分为 4 个并让 4 个线程填充显示是个好主意吗?

【问题讨论】:

    标签: ruby-on-rails ruby optimization query-optimization


    【解决方案1】:

    你应该写成;

    def index
      @display = Varient.joins(:product).where(is_active: true).order(:price).select('products.id as id, products.title, products.description, price, quantity, variants.id as variant_id')
    end
    

    【讨论】:

    • 我真的尝试过这样使用它,rails 说 f 是谁“价格”没有这样的列(我在选择中添加了 variables.price)
    • 加上它产生这个查询 SELECT products.id as id, products.title, products.description,variants。 price, variables.quantity, variables.id as variant_id FROM "products" WHERE "products"."is_active" = 't' ORDER BY "products"."price" ASC (no join)
    • @display = Product.select('products.id as id, products.title, products.description, variables.price, variant.quantity, variant.id as variant_id').joins(:variants ).where("variants.is_active = ?", 'true').order("variants.price") 这被执行但什么也不返回。
    • 这将改变结果 - 例如,没有变体的产品将不会被退回
    【解决方案2】:

    您的代码没有使用预先加载的数据,这就是添加包含会减慢速度的原因 - 这只是额外的工作。

    通常,如果您调用查询方法(whereorder 等),rails 无法使用预先加载的数据。相反,您可以创建一个新关联:

    has_many :active_variants,  -> { where(is_active: true).order(:price) }, class_name: "Variant"
    

    然后急切加载并使用此关联而不是变体关联。

    【讨论】:

    • 你的答案是完美的。我有一个请求。您能否演示我如何使用 :active_variants 与 include 的关系,因为我尝试使用 @products = Product.all.includes(:active_variants) 并且我得到未初始化的常量 Product::ActiveVariant
    • 糟糕 - 遗漏了一个关键点!
    • 它确实有效。但它再次增加了时间:)) 从 1.800ms 到 4,600ms 。但我不能拒绝你的回答
    • 确保你也放弃了选择。
    • 在你告诉我之前我很遗憾地做到了:(
    猜你喜欢
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多