gpt4 book ai didi

ruby-on-rails - 预先加载、模型方法、fields_for、each 和 N+1

转载 作者:数据小太阳 更新时间:2023-10-29 08:08:05 24 4
gpt4 key购买 nike

我有一个相当复杂的应用程序(超过 30 个表),它有一些持续的 N+1 问题,我假设这是因为我没有做一些“Rails Way™”

我将举一个更复杂的例子。这包括四个表:clins、positions_tasks、taskslabor_hours

positions_tasksclins、positions(本例不需要)和 tasks< 之间的三向多对多连接,它 有_many :labor_hours。 labor_hours 表有一个整数字段,表示一年中的每个月和一些其他数据。 total_hours 方法将所有月份加总为一年的总小时数。在 clin View 中,它显示了 clin 信息和所有相关任务的表格 [以及其他相关数据],并对每个任务的小时数求和,其中 has_many :labor_hours, :through => :positions_tasks。我急于加载所有相关表,包括 labor_hours,除 labor_hours 外,所有 N+1 问题都消失了。

后面是代码片段。

clins_controller 的预加载:

@clin = Clin.includes(:proposal).includes(:positions_tasks).includes(:tasks).includes(:labor_hours).includes(:wbss).find(params[:id])`

显示 clins/_form.html.erb 中的表格行:

    <tbody>
<% @clin.tasks.distinct.each do |t| %>
<%= f.fields_for :task, t do |builder| %>
<%= render "tasks/task_row", f: builder %>
<% end %>
<% end %>
</tbody>

_task_row 部分:

<tr>
<td><%= f.object.wbs_line_item.wbs.wbs_title %></td>
<td><%= f.object.wbs_line_item.wbs_line_item %></td>
<td><%= f.object.description %></td>
<td><%= f.object.labor_hours.distinct.each.sum(&:total_hours) %>
<td><div id="jump">
<%= link_to "Edit", {:controller => :tasks, :action => :edit, :id => f.object.id } %>
</div></td>
</tr>

临床模型:

class Clin < ActiveRecord::Base
nilify_blanks

belongs_to :proposal

belongs_to :parent, :class_name => "Clin"
has_many :children, :class_name => "Clin"

has_many :positions_tasks
has_many :labor_hours, :through => :positions_tasks
has_many :tasks, :through => :positions_tasks
has_many :wbs_line_items, :through => :tasks
has_many :wbss, :through => :wbs_line_items
has_many :pws_line_items, :through => :wbs_line_items
has_many :pwss, :through => :wbss
end

工时模型:

class LaborHours < ActiveRecord::Base
nilify_blanks

belongs_to :positions_task
belongs_to :year

has_one :proposal, :through => :positions_task
has_many :valid_years, :through => :proposal, :source => :years

def total_hours
m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 + m11 + m12
end
end

positionsTask 模型:

class PositionsTask < ActiveRecord::Base
nilify_blanks

belongs_to :task
belongs_to :position
belongs_to :clin

has_many :labor_hours

has_one :company, :through => :position
has_one :proposal, :through => :clin
has_one :wbs_line_item, :through => :task
delegate :wbs, :to => :wbs_line_item

delegate :pws_line_items, :to => :wbs_line_item
delegate :pwss, :to => :wbs_line_item

validates_presence_of :task
validates_presence_of :position
validates_presence_of :clin

accepts_nested_attributes_for :labor_hours, allow_destroy: true
end

任务模型:

class Task < ActiveRecord::Base
nilify_blanks

belongs_to :wbs_line_item
belongs_to :task_category

has_many :positions_tasks

has_many :labor_hours, :through => :positions_tasks
has_many :positions, :through => :positions_tasks
has_many :clins, :through => :positions_tasks
has_many :proposals, :through => :positions_tasks

delegate :wbs, :to => :wbs_line_item
delegate :pws_line_items, :to => :wbs_line_item
delegate :pwss, :to => :wbs

accepts_nested_attributes_for :positions_tasks, allow_destroy: true
accepts_nested_attributes_for :labor_hours, allow_destroy: true

validates_associated :positions_tasks

end

GET 和 SQL 加载:

Started GET "/clins/11/edit" for 127.0.0.1 at 2015-07-20 17:48:49 -0400
Processing by ClinsController#edit as HTML
Parameters: {"id"=>"11"}
Clin Load (0.2ms) SELECT "clins".* FROM "clins" WHERE "clins"."id" = $1 LIMIT 1 [["id", 11]]
Proposal Load (0.2ms) SELECT "proposals".* FROM "proposals" WHERE "proposals"."id" IN (1)
PositionsTask Load (0.4ms) SELECT "positions_tasks".* FROM "positions_tasks" WHERE "positions_tasks"."clin_id" IN (11)
Task Load (0.6ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" IN (1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 20, 23, 24)
LaborHours Load (1.1ms) SELECT "labor_hours".* FROM "labor_hours" WHERE "labor_hours"."positions_task_id" IN (1, 2, 3, 6, 7, 8, 9, 10, 12, 13, 14, 18, 19, 20, 21, 23, 24, 25, 26, 27, 30, 35, 36, 37)
WbsLineItem Load (0.5ms) SELECT "wbs_line_items".* FROM "wbs_line_items" WHERE "wbs_line_items"."id" IN (310, 312, 314, 316, 317, 318, 319, 413, 320, 321, 322, 324, 325, 326, 327, 328, 330, 333, 334)
Wbs Load (0.4ms) SELECT "wbss".* FROM "wbss" WHERE "wbss"."id" IN (1)
Clin Load (0.2ms) SELECT "clins".* FROM "clins"
Rendered tasks/_task_header.html.erb (0.0ms)
LaborHours Load (0.4ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 1]]
Rendered tasks/_task_row.erb (2.6ms)
LaborHours Load (0.3ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 2]]
Rendered tasks/_task_row.erb (1.7ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 3]]
Rendered tasks/_task_row.erb (1.4ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 5]]
Rendered tasks/_task_row.erb (1.3ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 6]]
Rendered tasks/_task_row.erb (1.4ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 7]]
Rendered tasks/_task_row.erb (1.5ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 8]]
Rendered tasks/_task_row.erb (1.3ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 9]]
Rendered tasks/_task_row.erb (1.3ms)
LaborHours Load (0.4ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 10]]
Rendered tasks/_task_row.erb (1.9ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 11]]
Rendered tasks/_task_row.erb (1.5ms)
LaborHours Load (0.4ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 12]]
Rendered tasks/_task_row.erb (2.2ms)
LaborHours Load (0.5ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 14]]
Rendered tasks/_task_row.erb (2.6ms)
LaborHours Load (0.4ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 15]]
Rendered tasks/_task_row.erb (2.2ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 16]]
Rendered tasks/_task_row.erb (1.5ms)
LaborHours Load (0.3ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 17]]
Rendered tasks/_task_row.erb (1.9ms)
LaborHours Load (0.3ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 18]]
Rendered tasks/_task_row.erb (1.6ms)
LaborHours Load (0.3ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 20]]
Rendered tasks/_task_row.erb (1.9ms)
LaborHours Load (0.2ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 23]]
Rendered tasks/_task_row.erb (1.6ms)
LaborHours Load (0.3ms) SELECT "labor_hours".* FROM "labor_hours" INNER JOIN "positions_tasks" ON "labor_hours"."positions_task_id" = "positions_tasks"."id" WHERE "positions_tasks"."task_id" = $1 [["task_id", 24]]
Rendered tasks/_task_row.erb (1.9ms)
Rendered clins/_form.html.erb (47.6ms)
Rendered clins/_errors.html.erb (0.0ms)
Rendered clins/edit.html.erb within layouts/application (48.6ms)
Rendered layouts/_header.html.erb (60.5ms)
Rendered layouts/_sidenav.html.erb (0.4ms)
Rendered layouts/_footer.html.erb (0.0ms)
Completed 200 OK in 140ms (Views: 106.4ms | ActiveRecord: 8.8ms)

我认为正在发生的事情是预加载在 _form.html.erb 中的 distinct.eachfields_for 中丢失,因为它正在传递任务对象而不是 clin 对象,和/或对 total_hours 的调用导致调用它的每个对象的负载,但我不确定如何确定它是哪个对象,也不知道如何解决它们。

如何在不加载 labor_hours 的情况下为表中的每个任务提供汇总的 task.labor_hours.total_hours

最佳答案

我不确定,但我有一个理论。你有这个(我已经删除了我们现在不关心的 includes 调用:

@clin = Clin.includes(:tasks).includes(:labor_hours).find(params[:id])

您在这里所做的是预先加载与每个 Clin 关联的 Tasks 和与每个 Clin 关联的 LaborHours — 到目前为止,还不错,但在您看来您正在这样做(或多或少):

@clin.tasks.distinct.each do |task|
# inside the partial...
task.labor_hours...
end

在这里,您访问的不是与每个 Clin 相关联的 LaborHours(这是您急切加载的内容),而是访问与每个 Clin 关联的每个 Task 相关联的 LaborHours。要访问与每个 Clin 关联的 LaborHours,您必须执行以下操作:

@clin.labor_hours.each do |labor_hour|
# ...
end

但是由于您正在呈现任务(而不仅仅是 LaborHours),我认为这不是您想要的。相反,您需要告诉 Rails 您想要预加载二阶关联——即与 Tasks 关联的 LaborHours,而不是与 Clins 关联的 LaborHours——通过将散列传递给 includes:

@clin = Clin.includes(:tasks => :labor_hours).find(params[:id])

附言您还可以进行一些额外的改进——例如,看起来您实际上并没有使用 LaborHours 中的任何属性,您实际上只是使用了 total_hours 列的总和。但是当你可以让数据库来做时,在 Ruby 中计算总和是一种浪费。但是,这超出了本答案的范围。

关于ruby-on-rails - 预先加载、模型方法、fields_for、each 和 N+1,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31527000/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com