Giả sử bạn cần lấy tên người dùng của năm bài đăng đầu tiên. Bạn nhanh chóng viết câu truy vấn bên dưới và tận hưởng kỳ nghỉ cuối tuần của mình.
posts = Post.limit(5)
posts.each do |post|
puts post.user.name
end
Tốt. Nhưng hãy xem xét các truy vấn
Post Load (0.5ms) SELECT `posts`.* FROM `posts` LIMIT 5
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
1 query
để tìm nạp tất cả posts
và 1 query
để tìm nạp users
cho mỗi bài đăng dẫn đến tổng số 6 queries
. Hãy xem giải pháp dưới đây thực hiện điều tương tự, chỉ trong 2 queries
:
posts = Post.includes(:user).limit(5)
posts.each do |post|
puts post.user.name
end
#####
Post Load (0.3ms) SELECT `posts`.* FROM `posts` LIMIT 5
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 2)
Có một sự khác biệt nhỏ. Thêm includes(:posts)
cho truy vấn của bạn và vấn đề đã được giải quyết. Nhanh chóng, tốt đẹp và dễ dàng.
Nhưng đừng chỉ thêm includes
trong truy vấn của bạn mà không hiểu đúng về nó. Sử dụng includes
với joins
có thể dẫn đến kết hợp chéo tùy thuộc vào tình huống và bạn không cần điều đó trong hầu hết các trường hợp.
Nếu bạn muốn thêm các điều kiện vào các mô hình đi kèm, bạn sẽ phải tham chiếu rõ ràng chúng . Ví dụ:
User.includes(:posts).where('posts.name = ?', 'example')
Sẽ xuất hiện một lỗi, nhưng điều này sẽ hoạt động:
User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
Lưu ý rằng includes
hoạt động với association names
trong khi references
cần the actual table name
.