减少N + 1个查询

时间:2015-03-04 06:34:35

标签: ruby-on-rails ruby ruby-on-rails-3 postgresql ruby-on-rails-4

N + 1查询检测到这意味着我该怎么做才能使其正常工作。

我使用Bullet gem来显示N + 1个查询

user: kalyan
N+1 Query detected
  Emp => [:functions]
  Add to your finder: :include => [:functions]
N+1 Query method call stack
  /home/Documents/app/views/tics/show.html.haml:20:in `_app_views_tics_show_html_haml___2301551533476719406_237290860'

这是来自bullet gem的消息。

_app_views_tics_show.html.haml

- if (@tic.assigned_to == current_user.emp) or (current_user.emp_functions.map{|x| x.id}.include?(1) if current_user.emp_functions.present? )
              = best_in_place @tic, :subject
            - else
              = @tic.subject

帮我减少n + 1查询问题

emp.rb

has_many :emp_functions, inverse_of: :emp,dependent: :restrict_with_exception

belongs_to:user,inverse_of :: emp

emp_functions.rb

belongs_to :emp , inverse_of: :emp_functions

belongs_to :function

function.rb

has_many :emp_functions, dependent: :restrict_with_exception

has_many :emp, through: :emp_functions

user.rb

has_one :emp, inverse_of: :user, dependent: :restrict_with_exception

3 个答案:

答案 0 :(得分:1)

Imho,摆脱这个N + 1查询的最有效方法是改变current_user方法,将includes(:emp_functions)添加到User.find调用。您的方式取决于您处理身份验证的方式。如果您正在使用某种类型的gem(例如DeviseSorcery),则需要重新打开这些类并更改方法,而不是忘记使用super。如果你自己编写了自己的身份验证,那么你就可以更容易地做到这一点。

我注意到的第二件事是,您实际上不需要在视图中的map上使用user.emp_functions,前提是emp_functionshas_many关联的User current_user.emp_function_ids.include?(1)模型。您可以将其展平为emp_functions。这将帮助您摆脱N + 1查询问题,但仅限于此特定情况。如果您经常在很多地方处理当前用户的{{1}},我建议使用我描述的第一条路径。

第三件事,与问题没有直接关系的是我真的不认为代码属于视图。您应该将其移动到帮助器或装饰器,使视图保持清洁和可读。

答案 1 :(得分:0)

如果emp_functions是您用户模型上的has_many关联(似乎是这样),那么您应该这样做:

current_user.emp_function_ids.include?(1)

答案 2 :(得分:0)

这是针对您的问题的一个非常具体的解决方案,我建议不要在视图中使用它

current_user.emp_functions.map{|x| x.id}.include?(1)更改为

current_user.emp_functions.where(id: 1).exists?

每当您点击包含它的页面时,这将导致对数据库进行1次单一查询。

您还可以删除if语句以删除另一个数据库查询。