app/controllers/test_controller.rb
class TestController < ApplicationController
def index
@companys = Company.includes(:employees).all
end
end
app/views/test/index.html.erb
<table>
<thead>
<tr>
<th>id</th>
<th>名前</th>
<th>従業員id</th>
<th>従業員氏名</th>
</tr>
</thead>
<tbody>
<% @companys.each do |company| %>
<% company.employees.each_with_index do |employee, index| %>
<tr>
<% if index == 0 %>
<td rowspan="<%= company.employees.count %>"><%= company.id %></td>
<td rowspan="<%= company.employees.count %>"><%= company.name %></td>
<% end %>
<td><%= employee.id %></td>
<td><%= employee.name %></td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
説明
\n1includesメソッドとN+1問題
\nincludesを使用することで、紐づいたモデルのデータをまとめて取得し、パフォーマンスを改善することができます。
\n \nポイント
\n N+1問題とは? includesは記入しなくても動作しますが、紐づいたモデルのデータを取得するたびにクエリを発行してしまう(N+1)問題が発生します。includesを使用すると、紐づいたデータをまとめて取得することができるため、N+1問題を解決することができます。
\n2基本的な使い方
\nincludesは以下のような形式で記述します:
\n \n\n
\n \n モデル.includes(:取得するモデルのフィールド).その他のメソッド\n
例えば、CompanyモデルとEmployeeモデルが関連している場合:
\n \n\n
\n \n # N+1問題が発生する例\ncompanies = Company.all\ncompanies.each do |company|\n puts company.employees.count # 会社ごとに別のクエリが発行される\n\n# includesを使用して最適化した例\ncompanies = Company.includes(:employees).all\ncompanies.each do |company|\n puts company.employees.count # 追加のクエリは発行されない\n
上の例ではCompanyを1側、Employeeを多側としてincludesを使用しています。
\n\n\n\n3複数の関連モデルを取得
\n複数のモデルのフィールドを取得したいときはincludesの引数を,区切りで増やすことができます:
\n \n\n
\n \n Employee.includes(:company, :department).all\n
この例では、各Employeeに関連するCompanyとDepartmentの情報が一度に取得されます。
\n\n\n\n4ネストした関連を取得
\n紐づいたモデルのさらに先の紐づいたデータを集計するときは、以下のように記述します:
\n \n\n
\n \n モデル.includes(取得するモデルのフィールド: :もう一つ先のフィールド)\n
例えば、Employeeが1側でそれに多側のFamilyが紐づいていた場合は:
\n \n\n
\n \n Company.includes(employees: :families).all\n
さらにFamilyが1側でそれに多側のFavoriteが紐づいていた場合は:
\n \n\n
\n \n Company.includes(employees: {families: :favorites})\n このように{}で入れ子にしていくことで、複数階層の関連を一度に取得できます。
\n\n\n\n5includesと他のメソッドの組み合わせ
\nincludesは他のActiveRecordメソッドと組み合わせて使用できます:
\n \n\n
\n\n\n\n # whereと組み合わせる\nCompany.includes(:employees).where(employees: { department: "営業部" })\n\n# orderと組み合わせる\nCompany.includes(:employees).order("companies.name ASC")\n\n# limitと組み合わせる\nCompany.includes(:employees).limit(10)\n\n# selectと組み合わせる (必要なカラムのみ取得)\nCompany.includes(:employees).select("companies.id, companies.name")\n 6実践的な使用例
\nコントローラーでの実際の使用例:
\n \n\n
\n \n class CompaniesController < ApplicationController\n def index\n # 基本的な使用例\n @companies = Company.includes(:employees).all\n end\n \n def show\n @company = Company.includes(employees: [:department, :projects]).find(params[:id])\n # これにより、ビューで@company.employeesやその関連データにアクセスしても追加クエリが発行されない\n end\n \n def dashboard\n # 複雑な関連を一度に取得\n @companies = Company.includes(\n employees: [\n :department,\n { projects: :tasks },\n { families: :favorites }\n ]\n ).all\n end\nend\n ビューでの例(app/views/companies/show.html.erb):
\n \n\n
\n\n\n<h1><%= @company.name %></h1>\n\n<h2>従業員一覧</h2>\n<ul>\n <% @company.employees.each do |employee| %>\n <li>\n <%= employee.name %> - <%= employee.department.name %>\n \n <h3>プロジェクト</h3>\n <ul>\n <% employee.projects.each do |project| %>\n <li><%= project.name %></li>\n <% end %>\n </ul>\n \n <h3>家族</h3>\n <ul>\n <% employee.families.each do |family| %>\n <li>\n <%= family.name %>\n <p>好きなもの: <%= family.favorites.map(&:name).join(', ') %></p>\n </li>\n <% end %>\n </ul>\n </li>\n <% end %>\n</ul>\n ポイント
\n パフォーマンス最適化のヒント:
\n- \n
- includesは内部的に
LEFT OUTER JOINまたはPRELOADを使用してデータを取得します。 \n - 大量のデータを扱う場合、必要な関連のみをincludesに指定するようにしましょう。 \n
- 関連が非常に深い場合や複雑な場合は、パフォーマンスに影響が出る可能性があります。適切な範囲で使用しましょう。 \n
- 開発中に発行されるSQLクエリを確認するには、Railsコンソールで
ActiveRecord::Base.logger = Logger.new(STDOUT)を実行すると便利です。 \n - より複雑な条件で関連データを読み込む場合は、
joinsメソッドの使用も検討してください。 \n