Xét ví dụ: bảng zombies có quan hệ một nhiều với bảng weapons như sau:

    so sánh join, preload và eager load trong rails

 

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

http://blog.scoutapp.com/articles/2017/01/24/activerecord-includes-vs-joins-vs-preload-vs-eager_load-when-and-where