Rails ActiveAdmin 消除对属于另一个模型的自定义属性的 n+1 个查询
Rails ActiveAdmin Eliminating n+1 queries on custom attributes belong to another model
我正在使用 activeadmin 来显示分支模型记录,分支模型有很多分支phone,我想显示为这个分支创建的第一个分支phone作为额外的自定义activeadmin分支模型屏幕上的属性,所以我这样写的
show do
attributes_table do
row :id
row :manager_name
row :manager_email
row :phone_number do |branch|
branch&.branch_phones&.order(created_at: :asc)&.first&.phone_number
end
end
end
index do
column :id
column :manager_name
column :manager_email
column :phone_number do |branch|
branch&.branch_phones&.order(created_at: :asc)&.first&.phone_number
end
actions
end
该代码的问题在于它会导致我进行 n+1 次查询,每次代码获得一个分支时,它都会在其中创建一个额外的查询以获取该分支的分支 phones,这在 activeadmin
上调用分支模型屏幕时会出现类似的结果
Processing by Admin::BranchesController#index as HTML
Parameters: {"subdomain"=>""}
AdminUser Load (0.8ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = ORDER BY "admin_users"."id" ASC LIMIT [["id", 1], ["LIMIT", 1]]
Rendering /usr/local/bundle/gems/activeadmin-2.7.0views/active_admin/resource/index.html.arb
(0.6ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT OFFSET ) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
CACHE (0.1ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT OFFSET ) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
(0.4ms) SELECT COUNT(*) FROM "branches"
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT OFFSET ) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
Branch Load (0.5ms) SELECT "branches".* FROM "branches" ORDER BY "branches"."id" desc LIMIT OFFSET [["LIMIT", 30], ["OFFSET", 0]]
Store Load (0.6ms) SELECT "stores".* FROM "stores" WHERE "stores"."id" IN (, , ) [["id", 21], ["id", 1], ["id", 2]]
BranchPhone Load (0.4ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" IN (, , , , , , , , , , ) [["branch_id", 25], ["branch_id", 24], ["branch_id", 22], ["branch_id", 20], ["branch_id", 19], ["branch_id", 14], ["branch_id", 11], ["branch_id", 4], ["branch_id", 3], ["branch_id", 2], ["branch_id", 1]]
BranchPhone Load (0.7ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 25], ["LIMIT", 1]]
BranchPhone Load (0.5ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 24], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 22], ["LIMIT", 1]]
BranchPhone Load (0.6ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 20], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 19], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 14], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 11], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 4], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 3], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 2], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 1], ["LIMIT", 1]]
Rendered /usr/local/bundle/gems/activeadmin-2.7.0views/active_admin/resource/index.html.arb (219.4ms)
Completed 200 OK in 258ms (Views: 208.7ms | ActiveRecord: 23.2ms)
那么,有什么解决方案可以消除分支 phone 的 n+1 查询吗?
知道我试图使用这样的控制器操作来消除它
但是分支phone的N+1查询还是没有解决
您收到 (N+1) 个查询,因为您正在订购 branch_phones
。默认 Rails returns 记录在同意顺序。您无需再次订购。
我相信你有一些像下面这样的联想:
# app/models/branch.rb
class Branch < ApplicationRecord
has_many :branch_phones
end
# app/models/branch_phone.rb
class BranchPhone < ApplicationRecord
belongs_to :branch
end
请将此行添加到您的 app/admin/brach.rb
文件中。
# app/admin/brach.rb
controller do
def scoped_collection
super.includes(:branch_phones)
end
end
index do
column :id
column :manager_name
column :manager_email
column :phone_number do |branch|
branch.branch_phones.first&.phone_number
end
actions
end
现在 includes 方法在 ActiveAdmin 中可用。您需要做的就是将其添加到所需的管理文件中。
# app/admin/brach.rb
ActiveAdmin.register Branch do
includes :branch_phones
index do
column :id
column :manager_name
column :manager_email
column :phone_number do |branch|
branch&.branch_phones&.order(created_at: :asc)&.first&.phone_number
end
actions
end
end
我正在使用 activeadmin 来显示分支模型记录,分支模型有很多分支phone,我想显示为这个分支创建的第一个分支phone作为额外的自定义activeadmin分支模型屏幕上的属性,所以我这样写的
show do
attributes_table do
row :id
row :manager_name
row :manager_email
row :phone_number do |branch|
branch&.branch_phones&.order(created_at: :asc)&.first&.phone_number
end
end
end
index do
column :id
column :manager_name
column :manager_email
column :phone_number do |branch|
branch&.branch_phones&.order(created_at: :asc)&.first&.phone_number
end
actions
end
该代码的问题在于它会导致我进行 n+1 次查询,每次代码获得一个分支时,它都会在其中创建一个额外的查询以获取该分支的分支 phones,这在 activeadmin
上调用分支模型屏幕时会出现类似的结果Processing by Admin::BranchesController#index as HTML
Parameters: {"subdomain"=>""}
AdminUser Load (0.8ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = ORDER BY "admin_users"."id" ASC LIMIT [["id", 1], ["LIMIT", 1]]
Rendering /usr/local/bundle/gems/activeadmin-2.7.0views/active_admin/resource/index.html.arb
(0.6ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT OFFSET ) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
CACHE (0.1ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT OFFSET ) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
(0.4ms) SELECT COUNT(*) FROM "branches"
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT OFFSET ) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
Branch Load (0.5ms) SELECT "branches".* FROM "branches" ORDER BY "branches"."id" desc LIMIT OFFSET [["LIMIT", 30], ["OFFSET", 0]]
Store Load (0.6ms) SELECT "stores".* FROM "stores" WHERE "stores"."id" IN (, , ) [["id", 21], ["id", 1], ["id", 2]]
BranchPhone Load (0.4ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" IN (, , , , , , , , , , ) [["branch_id", 25], ["branch_id", 24], ["branch_id", 22], ["branch_id", 20], ["branch_id", 19], ["branch_id", 14], ["branch_id", 11], ["branch_id", 4], ["branch_id", 3], ["branch_id", 2], ["branch_id", 1]]
BranchPhone Load (0.7ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 25], ["LIMIT", 1]]
BranchPhone Load (0.5ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 24], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 22], ["LIMIT", 1]]
BranchPhone Load (0.6ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 20], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 19], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 14], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 11], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 4], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 3], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 2], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = ORDER BY "branch_phones"."created_at" ASC LIMIT [["branch_id", 1], ["LIMIT", 1]]
Rendered /usr/local/bundle/gems/activeadmin-2.7.0views/active_admin/resource/index.html.arb (219.4ms)
Completed 200 OK in 258ms (Views: 208.7ms | ActiveRecord: 23.2ms)
那么,有什么解决方案可以消除分支 phone 的 n+1 查询吗?
知道我试图使用这样的控制器操作来消除它
但是分支phone的N+1查询还是没有解决
您收到 (N+1) 个查询,因为您正在订购 branch_phones
。默认 Rails returns 记录在同意顺序。您无需再次订购。
我相信你有一些像下面这样的联想:
# app/models/branch.rb
class Branch < ApplicationRecord
has_many :branch_phones
end
# app/models/branch_phone.rb
class BranchPhone < ApplicationRecord
belongs_to :branch
end
请将此行添加到您的 app/admin/brach.rb
文件中。
# app/admin/brach.rb
controller do
def scoped_collection
super.includes(:branch_phones)
end
end
index do
column :id
column :manager_name
column :manager_email
column :phone_number do |branch|
branch.branch_phones.first&.phone_number
end
actions
end
现在 includes 方法在 ActiveAdmin 中可用。您需要做的就是将其添加到所需的管理文件中。
# app/admin/brach.rb
ActiveAdmin.register Branch do
includes :branch_phones
index do
column :id
column :manager_name
column :manager_email
column :phone_number do |branch|
branch&.branch_phones&.order(created_at: :asc)&.first&.phone_number
end
actions
end
end