問題が発生している様子
画面を読み込んでいるときのターミナル
Terminal
Processing by PagesController#index as HTML
Rendering layout layouts/application.html.slim
Rendering pages/index.html.slim within layouts/application
Product Load (0.9ms) SELECT `products`.* FROM `products`
↳ app/views/pages/index.html.slim:25
Brand Load (0.9ms) SELECT `brands`.* FROM `brands` WHERE `brands`.`id` = 1 LIMIT 1
↳ app/views/pages/index.html.slim:30
Brand Load (0.9ms) SELECT `brands`.* FROM `brands` WHERE `brands`.`id` = 12 LIMIT 1
↳ app/views/pages/index.html.slim:30
Brand Load (0.8ms) SELECT `brands`.* FROM `brands` WHERE `brands`.`id` = 15 LIMIT 1
↳ app/views/pages/index.html.slim:30
Brand Load (0.9ms) SELECT `brands`.* FROM `brands` WHERE `brands`.`id` = 2 LIMIT 1
↳ app/views/pages/index.html.slim:30
Brand Load (0.8ms) SELECT `brands`.* FROM `brands` WHERE `brands`.`id` = 6 LIMIT 1
↳ app/views/pages/index.html.slim:30
Brand Load (0.8ms) SELECT `brands`.* FROM `brands` WHERE `brands`.`id` = 3 LIMIT 1
↳ app/views/pages/index.html.slim:30
Rendered pages/index.html.slim within layouts/application (Duration: 44.1ms | Allocations: 14640)
複数のクエリが発行されている。
最初にproductsテーブル中の全てを取り出すのに1回、その中からbrandを6回取り出している。
これは今後クエリ発生数が、何万回のように膨大になると動作が悪化するので対策をとる。
解決策
Controllerで、includesメソッドを使用する。(N+1問題が発生している箇所)
自分はallメソッドを使用していた。
app/controllers/pages_controller.rb
class PagesController < ApplicationController
def index
- @products = Product.all
+ @products = Product.includes(:brand)
end
end
includeメソッド基礎構文
モデル名.include(:関連名)
- 関連名 ≠ テーブル名 (下記 app/models/product.rb 参照)
対策した結果
Terminal
Processing by PagesController#index as HTML
Rendering layout layouts/application.html.slim
Rendering pages/index.html.slim within layouts/application
Product Load (0.9ms) SELECT `products`.* FROM `products`
↳ app/views/pages/index.html.slim:25
Brand Load (1.1ms) SELECT `brands`.* FROM `brands` WHERE `brands`.`id` IN (1, 12, 15, 2, 6, 3)
↳ app/views/pages/index.html.slim:25
Rendered pages/index.html.slim within layouts/application (Duration: 24.3ms | Allocations: 10596)
Brandに関するクエリの発生数が一回になった。
その他詳細
コード
View
app/views/pages/index.html.slim
.container
.row
.col-12
table.table.table-hover.table--index-product
thead
(省略)
tbody
- @products.each do |product|
tr
th scope="row"
td
p = link_to product.name, product
p = link_to product.brand.name, product.brand
td.d-none.d-md-table-cell
p = link_to product.battery_capacity, product
td
p = link_to product.soc_antutu_score, product
Model
app/models/brand.rb
class Brand < ApplicationRecord
has_many :products
end
app/models/product.rb
class Product < ApplicationRecord
belongs_to :brand #関連名
validates :brand_id, presence: true
end
・belongs_toメソッドの引数の関連名は単数形
Controller
Pages_controller.rb
class PagesController < ApplicationController
def index
@products = Product.includes(:brand)
end
end
参考URL
includesメソッド
Active Record クエリインターフェイス - Railsガイド- 【Rails】N+1問題をincludesメソッドで解決しよう! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト