Оптимизация запросов в Rails — ключевой навык для разработчиков, работающих с PostgreSQL. В этой статье разбираемся, как правильно использовать методы ActiveRecord (joins, includes, preload, eager_load), чтобы избежать проблем с производительностью и N+1 запросами.
Когда запросы становятся медленными, а лог растет на десятки строк — пора разбираться, чем отличаются joins, includes и preload в ActiveRecord.
🤝 joins
Создаёт SQL JOIN, но не загружает связанные записи в память.
User.joins(:company).where(companies: { active: true })
🔎 SQL:
SELECT "users".* FROM "users"
INNER JOIN "companies" ON "companies"."id" = "users"."company_id"
WHERE "companies"."active" = TRUE
- Работает быстро
- Позволяет фильтровать по ассоциациям
- Но
user.companyвызовет доп. запрос
📦 includes
Подгружает ассоциации, чтобы избежать N+1:
User.includes(:company).each { |u| puts u.company.name }
🔎 SQL:
SELECT "users".* FROM "users";
SELECT "companies".* FROM "companies" WHERE "companies"."id" IN (...)
ActiveRecord решает сам, использовать ли JOIN или отдельный SELECT. Иногда это неожиданно приводит к eager_load.
🚚 preload
Заставляет ActiveRecord загрузить ассоциации через отдельный запрос:
User.preload(:company)
🔎 SQL:
SELECT "users".* FROM "users";
SELECT "companies".* FROM "companies" WHERE "companies"."id" IN (...)
- Никогда не делает JOIN
- Работает стабильно и предсказуемо
🔍 eager_load
Это includes + JOIN, независимо от условий:
User.eager_load(:company).where(companies: { name: "Acme" })
🔎 SQL:
SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...,
"companies"."id" AS t1_r0, "companies"."name" AS t1_r1, ...
FROM "users"
LEFT OUTER JOIN "companies" ON "companies"."id" = "users"."company_id"
WHERE "companies"."name" = 'Acme'
- Ассоциации загружаются сразу
- Выглядит как
SELECT ... LEFT OUTER JOIN
💡 Когда использовать что
| Задача | Метод |
|---|---|
| Нужно фильтровать по полям ассоциации | joins |
| Нужно избежать N+1 | includes или preload |
| Нужно всё в одном запросе | eager_load |
| Нужно 100% предсказуемо | preload |
🔚 Вывод: если не хочешь сюрпризов — используй preload. А joins — твой друг, когда нужен SQL-контроль.