渴望加载与复杂查询的关联

时间:2014-02-14 13:34:07

标签: ruby-on-rails-3 performance postgresql activerecord eager-loading

使用PostgreSQL的Rails 3.2.13

我们正在尝试优化以下模型代码,该代码根据用户定义的过滤条件从数据库中检索一组电子商务订单。

用户可以选择根据订单状态,付款状态&amp;发货状态。他们可以将这些设置为==<>每个特定值。

在此之后,我们过滤掉任何数字订单,然后对结果进行分页。

这一切都运作良好,除了订单有很多关联,例如产品,地址,付款等。这会导致N + 1个查询并使进程极慢。

Order.rb:

belongs_to :shop
has_many :shipping_lines , :dependent => :destroy
has_many :line_items , :dependent => :destroy
has_many :taxlines , :dependent => :destroy
has_many :fulfillments , :dependent => :destroy
has_many :notes , :dependent => :destroy
has_many :discounts , :dependent => :destroy
has_one :billing_address , :dependent => :destroy
has_one :shipping_address , :dependent => :destroy
has_one :payment_detail, :dependent => :destroy
has_many :order_histories, :dependent => :destroy

关于我们如何在下面的查询中包含急切加载的关联的任何想法?

示例输入:用户可能希望查看任何(已开启或已关闭)尚未发货的付费订单

Order Status == 'open'

Payment Status == 'any'

Shipping Status <> 'shipped'

商店由子域

标识
def by_filter(subdomain, filter,page)
  shop_condition = "shop_id = #{subdomain.shops[0].id}"
  if filter.name != "All"
    if filter.order_status == "any"
      status_condition =  "status in (?)"
      order_status = Filter::ORDER_STATUS.values - ["any"]
    else
      status_condition =  "status #{Filter::STATUS_OPERATORS_MAPPING[filter.order_status_operator]} ?"
      order_status = filter.order_status
    end

    if filter.payment_status == "any"
      payment_status_condition =  "payment_status in (?)"
      payment_status = Filter::PAYMENT_STATUS.values - ["any"]
    else
      payment_status_condition =  "payment_status #{Filter::STATUS_OPERATORS_MAPPING[filter.payment_status_operator]} ?"
      payment_status = filter.payment_status
    end

    if filter.fulfillment_status == "any"
      fulfillment_status_condition =  "fulfillment_status in (?)"
      fulfillment_status = Filter::FULFILLMENT_STATUS.values - ["any"]
    else
      fulfillment_status_condition = "fulfillment_status #{Filter::STATUS_OPERATORS_MAPPING[filter.fulfillment_status_operator]} ?"
      fulfillment_status = filter.fulfillment_status
    end

    conditions = "#{status_condition} AND #{payment_status_condition} AND #{fulfillment_status_condition} AND #{shop_condition} "
  else
    conditions = shop_condition
  end


  orders = self.where([conditions, order_status, payment_status, fulfillment_status]).order("order_created_at #{filter.sort_by}")


  if filter.digital_orders
    orders_arr = ((orders.all.collect(&:shipping_address) - [nil]).collect(&:order)).flatten
    orders = Kaminari.paginate_array(orders_arr).page(page).per(filter.show)
  else
    orders = orders.page(page).per(filter.show)
  end
  return orders
end

生成的SQL查询如下所示:

SELECT "orders".* FROM "orders" WHERE (status = 'open' AND payment_status in ('authorized', 'pending', 'paid', 'partially_paid', 'partially_refunded', 'refunded', 'voided') AND fulfillment_status <> 'shipped' AND shop_id = 58 ) ORDER BY order_created_at DESC

我们已经尝试过常规.includes但这似乎没有效果。例如:

orders = self.includes(:shipping_address).where([conditions, order_status, payment_status, fulfillment_status]).order("order_created_at #{filter.sort_by}")

1 个答案:

答案 0 :(得分:1)

使用命名范围,而不是定义Order模型的方法。

class Order

named_scope :with_address,
:include    => :shipping_address

...

我也会使用范围进行过滤和排序。

您可以阅读更多关于:include和:来自http://venkatev.wordpress.com/2010/02/04/activerecord_joins_and_include/

的联接