如何避免N + 1查询

时间:2015-07-31 18:12:05

标签: sql ruby-on-rails activerecord

我有以下型号:

  • 用户
  • 能力
  • PricingRule

使用以下关系定义:

  • 用户有许多定价规则
  • 能力有一个定价规则

enter image description here

我们的想法是获取符合某些条件的所有能力,并为每个能力获取其定价规则。但是,可以基于每个用户定义特定能力的自定义定价规则。

目前我获取所有匹配的能力并将其迭代到:

  • 尝试找到符合用户定价规则的当前能力
  • 或默认为该能力的定价规则

我正在使用Rails和ActiveRecord以及我目前所拥有的内容:

user = User.first
Ability.all.map do |a|
  user.pricing_rules.matching_ability(a).first || a.pricing_rule
end

每个用户定价规则定制应该由企业按需完成。常见的工作流程是从能力中获取定价规则。

非常感谢任何让我走上正轨的想法或帮助。

修改

matching_ability实施如下:

def self.matching_ability(ability)
  where(name: ability.name)
end

1 个答案:

答案 0 :(得分:2)

您可以“急切加载”以避免N + 1查询,如下所示:

user = User.includes(pricing_rules: :abilities).first
Ability.includes(:pricing_rule).map do |a|
  user.pricing_rules.matching_ability(a).first || a.pricing_rule
end

您应该在生成的SQL中看到这会为您的查询添加LEFT OUTER JOIN,因此ActiveRecord仅在两个查询中加载关联的记录。特别是,用户将在每个pricing_rules上加载abilitiespricing_rule,并且功能将加载pricing_rules

但是,使用matching_ability实施where可能会产生其他查询,让您回到N + 1问题。要利用第一个查询中的“预先加载”,您可能需要重构为:

self.matching_ability(ability)
  select{|a| a.name == ability.name}
end