includesしたのにN+1問題が回避できない

userモデルとtaskモデルに関して、1対多のアソシエーションを組んでおり、

以下のようにビューでeach構文の中でアソシエーションを使ってuserのtasksの数を表示させておりました。

app/views/admin/users/index.html.slim

div.fontsize_midium.margintop_big
| 管理画面:ユーザー一覧
span.span_margin_big.btn.light-blue.accent-2
= link_to "ユーザー新規作成", new_admin_user_path

- @users.each do |user|
div.margintop_big.card.fontsize_small.padding_midium
| ID:
= user.id
span.span_margin
= user.name
span.span_margin
= user.created_at
span.span_margin
| タスク数:
= user.tasks.count
span.span_margin
=link_to "編集", "/admin/users/#{user.id}/edit"
span.span_margin
= link_to "削除", "/admin/users/#{user.id}", method: :delete

 

N+1問題を防ぐために、コントローラーでincludes(:tasks)としておりました。

app/controllers/admin/users_controller.rb

def index
@users = User.all.order("created_at DESC").includes(:tasks)
end

 

しかし、ログを見てみると、N+1問題が発生してしまっておりました。。。

Started GET "/admin/users" for 127.0.0.1 at 2019-02-12 11:44:26 +0900
Processing by Admin::UsersController#index as HTML
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 3], ["LIMIT", 1]]
↳ app/helpers/sessions_helper.rb:8
Rendering admin/users/index.html.slim within layouts/application
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY created_at DESC
↳ app/views/admin/users/index.html.slim:6
Task Load (0.2ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."user_id" IN ($1, $2, $3) [["user_id", 3], ["user_id", 2], ["user_id", 1]]
↳ app/views/admin/users/index.html.slim:6
(0.3ms) SELECT COUNT(*) FROM "tasks" WHERE "tasks"."user_id" = $1 "user_id", 3
↳ app/views/admin/users/index.html.slim:16
(0.2ms) SELECT COUNT(*) FROM "tasks" WHERE "tasks"."user_id" = $1 "user_id", 2
↳ app/views/admin/users/index.html.slim:16
(0.2ms) SELECT COUNT(*) FROM "tasks" WHERE "tasks"."user_id" = $1 "user_id", 1
↳ app/views/admin/users/index.html.slim:16
Rendered admin/users/index.html.slim within layouts/application (7.3ms)
Completed 200 OK in 45ms (Views: 39.5ms | ActiveRecord: 1.6ms)

 

 

 

解決方法

user.tasks.countとしていた部分を、user.tasks.sizeと変更しました。

app/views/admin/users/index.html.slim

div.fontsize_midium.margintop_big
| 管理画面:ユーザー一覧
span.span_margin_big.btn.light-blue.accent-2
= link_to "ユーザー新規作成", new_admin_user_path

- @users.each do |user|
div.margintop_big.card.fontsize_small.padding_midium
| ID:
= user.id
span.span_margin
= user.name
span.span_margin
= user.created_at
span.span_margin
| タスク数:
= user.tasks.size #countをやめてsizeにした
span.span_margin
=link_to "編集", "/admin/users/#{user.id}/edit"
span.span_margin
= link_to "削除", "/admin/users/#{user.id}", method: :delete

 

すると、以下のように、N+1問題も解決しました!!

Started GET "/admin/users" for 127.0.0.1 at 2019-02-12 11:46:38 +0900
Processing by Admin::UsersController#index as HTML
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 3], ["LIMIT", 1]]
↳ app/helpers/sessions_helper.rb:8
Rendering admin/users/index.html.slim within layouts/application
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY created_at DESC
↳ app/views/admin/users/index.html.slim:6
Task Load (0.2ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."user_id" IN ($1, $2, $3) [["user_id", 3], ["user_id", 2], ["user_id", 1]]
↳ app/views/admin/users/index.html.slim:6
Rendered admin/users/index.html.slim within layouts/application (6.4ms)
Completed 200 OK in 38ms (Views: 34.2ms | ActiveRecord: 0.9ms)

 

countはrailsのメソッドというより、SQLのCOUNT関数的な挙動をしているようです。

*調べてみたところ、「COUNT関数はSELECT文により選択されたレコードの件数を返します」とのことでした。