модель
# app/models/user.rb
class User < ApplicationRecord
has_many :posts
end
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
end
Пример без использования includes
# Создаем пользователей и посты
users = (1..10).map { |i| User.create(name: "User #{i}") }
users.each do |user|
(1..5).each { |i| Post.create(title: "Post #{i}", user: user) }
end
# Проверяем первый пост у каждого пользователя без использования includes
users.each do |user|
puts user.posts.first
end
Пример с использованием includes
# Создаем пользователей и посты
users = (1..10).map { |i| User.create(name: "User #{i}") }
users.each do |user|
(1..5).each { |i| Post.create(title: "Post #{i}", user: user) }
end
# Проверяем первый пост у каждого пользователя с использованием includes
users_with_posts = User.includes(:posts).all
users_with_posts.each do |user|
puts user.posts.first
end
Результаты
Без использования includes: В этом случае для каждого пользователя будет выполнен отдельный запрос к базе данных для получения его постов. Если у вас 10 пользователей, то будет выполнено 11 запросов (1 запрос для получения пользователей и 10 запросов для получения постов).
С использованием includes: В этом случае будет выполнен только один запрос для получения всех пользователей и их постов. Общее количество запросов будет равно 2 (1 запрос для пользователей и 1 запрос для постов).
Вывод
Использование includes значительно сокращает количество запросов к базе данных, что улучшает производительность приложения.
require 'benchmark'
require 'active_support/core_ext/enumerable'
# Генерируем большой массив объектов
users = []
1_000_000.times do
users << { name: "User#{rand(1000)}", age: rand(18..65) }
end
# Измеряем время выполнения метода pluck
pluck_time = Benchmark.realtime do
names_pluck = users.pluck(:name)
end
# Измеряем время выполнения метода map
map_time = Benchmark.realtime do
names_map = users.map { |user| user[:name] }
end
# Преобразуем время в секунды с точностью до миллисекунд
pluck_time_seconds = pluck_time.round(6)
map_time_seconds = map_time.round(6)
puts "Время выполнения pluck: #{pluck_time_seconds} секунд"
puts "Время выполнения map: #{map_time_seconds} секунд"
Время выполнения pluck: 0.100913 секунд
SELECT "users"."name" FROM "users"
Время выполнения map: 0.150986 секунд
<span class="token keyword">SELECT</span> <span class="token string">"users"</span><span class="token punctuation">.</span><span class="token operator">*</span> <span class="token keyword">FROM</span> <span class="token string">"users"</span>
раньше просто не особо задумывался а сейчас попался интересный случай, делаю запрос в базу, find_by возвращает 1 объект, на пустой базе вернул релейшен, ну и дальше логика поплыла