Cả ba đều nạp trước association để tránh N+1, khác ở SQL sinh ra.
preload— luôn chạy query riêng cho mỗi association (SELECT ... WHERE post_id IN (...)). Không thể lọc theo cột của bảng liên kết.eager_load— gộp thành một query dùng LEFT OUTER JOIN. Lọc được theo cột bảng liên kết, nhưng dòng bị nhân bản và kết quả nặng hơn.includes— để Rails tự chọn: mặc định hành xử nhưpreload; nếu bạn tham chiếu bảng liên kết trongwhere(kèmreferences) thì nó chuyển sangeager_load.
ruby
Post.includes(:comments).where(comments: { spam: false }).references(:comments)
# => switches to LEFT OUTER JOIN
Post.preload(:comments) # always 2 queries
Post.eager_load(:comments) # always 1 query with LEFT OUTER JOINChọn: chỉ hiển thị dữ liệu liên kết → preload (hai query nhỏ thường nhanh hơn một join lớn). Cần lọc hoặc sắp xếp theo cột của bảng liên kết → eager_load. Không chắc → includes.
Gotcha hay bị hỏi: joins không nạp sẵn association — vẫn N+1 nếu sau đó truy cập post.comments. Dùng gem bullet ở môi trường dev để phát hiện.