Thận trọng là đứa con trưởng của sự khôn ngoan. Caution is the eldest child of wisdom.
Victor Hugo
Xét ví dụ: bảng zombies có quan hệ một nhiều với bảng weapons như sau:

1.Joins
joins là câu lệnh join 2 bảng bình thường trong sql:
Zombie.joins(:weapons)
#return 6 rows
#<ActiveRecord::Relation [
#<Zombie id: 1, name: "Ashley", graveyard: "Glen Haven Memorial Cemetery">,
#<Zombie id: 2, name: "Bob", graveyard: "Chapel Hill Cemetery">,
#<Zombie id: 3, name: "Katie", graveyard: "My Fathers Basement">,
#<Zombie id: 1, name: "Ashley", graveyard: "Glen Haven Memorial Cemetery">,
#<Zombie id: 1, name: "Ashley", graveyard: "Glen Haven Memorial Cemetery">,
#<Zombie id: 1, name: "Ashley", graveyard: "Glen Haven Memorial Cemetery">
#]>
Câu lệnh joins sẽ không load những bảng quan hệ ra bộ nhớ. Ví dụ đoạn code sau sẽ tạo nhiều câu query vào DB để lấy từng dữ liệu quan hệ:
@zombies = Zombie.joins(:weapons)
<% @zombies.each do |zombie| %>
<%= zombie.weapons.first.name %>
<% end %>
=begin
Select * from weapons where zombie_id = 1
Select * from weapons where zombie_id = 2
Select * from weapons where zombie_id = 3
Select * from weapons where zombie_id = 4
.......
=end
Tình trạng trên sẽ làm giảm performance. Để tránh tình trạng trên, ta có thể dùng 3 câu lệnh dưới đây.
2. Preload
preload() sẽ load cả những bảng quan hệ ra bộ nhớ, và câu lệnh SQL thực hiện tối đa chỉ là 2 thay vì nhiều câu lệnh như joins phía trên
Zombie.preload(:weapons).map{ |zombie| zombie.weapons.size } //return [4, 1, 1, 0]
=begin
SELECT "zombies".* FROM "zombies"
SELECT "weapons".* FROM "weapons" WHERE "weapons"."weapon_id" IN (1, 2, 3, 4)
=end
Nhưng preload() sẽ không hiểu câu lệnh where() với những bảng quan hệ. Ví dụ:
Zombie.preload(:weapons).where(weapons: {name: 'Hammer'} )
#<PG::UndefinedTable: ERROR: missing FROM-clause entry for table "weapons"
#LINE 1: SELECT "zombies".* FROM "zombies" WHERE "weapons"."name" = ...
# ^
Để dùng được câu lệnh where() với bảng quan hệ, ta có thể dùng includes()
3. Includes
includes() cũng tương tự với preload() nhưng có thể sử dụng where() với các bảng quan hệ.
Zombie.includes(:weapons).where(weapons: {name: 'Hammer'} )
#<ActiveRecord::Relation [#<Zombie id: 1, name: "Ashley", graveyard: "Glen Haven Memorial Cemetery">]>
Nếu có câu lệnh where() ở bảng quan hệ thì câu lệnh includes() sẽ gộp thành 1 câu query, còn nếu không thì sẽ tách thành 2 câu query:
Zombie.includes(:weapons)
=begin
SELECT "zombies".* FROM "zombies"
SELECT "weapons".* FROM "weapons" WHERE "weapons"."weapons_id" IN (1, 2, 3, 4)
=end
Zombie.includes(:weapons).where(weapons: {name: 'Hammer'} )
=begin
SELECT "zombies"."id" AS t0_r0, "zombies"."name" AS t0_r1, "zombies"."graveyard" as t0_r2, "weapons"."id" AS t1_r0, "weapons"."name" AS t1_r1, "weapons"."strength" AS t1_r2, "weapons"."zombie_id" AS t1_r3
FROM "zombies" LEFT OUTER JOIN "weapons" ON "weapons"."weapon_id" = "zombie"."id"
WHERE (weapon.name = "Hammer")
=end
Lưu ý: Khi sử dụng query là string trong hàm where đối với bảng quan hệ thì bạn phải tham chiếu tới bảng quan hệ nếu không sẽ bị báo lỗi:
Zombie.includes(:weapons).where("weapons.name = 'Hammer'").references(:weapons)
#<ActiveRecord::Relation [#<Zombie id: 1, name: "Ashley", graveyard: "Glen Haven Memorial Cemetery">]>
4. Eager_load
eager_load() giống với include(), điểm khác biệt là eager_load() luôn luôn sử dụng chỉ một câu lệnh query cho dù không có câu lệnh where() hay order() ở bảng quan hệ đi chăng nữa.
Zombie.eager_load(:weapons)
=begin
SELECT "zombies".* FROM "zombies" SELECT "weapons".* FROM "weapons"
WHERE "weapons"."weapons_id" IN (1, 2, 3, 4) Zombie.includes(:weapons).where(weapons: {name: 'Hammer'} ) #=> SELECT "zombies"."id" AS t0_r0, "zombies"."name" AS t0_r1, "zombies"."graveyard" as t0_r2, "weapons"."id" AS t1_r0, "weapons"."name" AS t1_r1, "weapons"."strength" AS t1_r2, "weapons"."zombie_id" AS t1_r3
FROM "zombies" LEFT OUTER JOIN "weapons" ON "weapons"."weapon_id" = "zombie"."id"
WHERE (weapon.name = "Hammer")
=end
Linh tham khảo:
http://blog.bigbinary.com/2013/07/01/preload-vs-eager-load-vs-joins-vs-includes.html
