- vừa được xem lúc

Exploring ActiveRecord eager loading methods

0 0 21

Người đăng: LamDN

Theo Viblo Asia

Khi sử dụng ActiveRecord để load dữ liệu, đôi lúc chúng ta sẽ bắt gặp những trường hợp mọi thứ không hoạt động như những gì ta mong muốn. Đó là do tuỳ vào từng điều kiện, ActiveRecord sẽ lựa chọn hoặc kết hợp các phương thức eager loading lại với nhau để thực thi truy vấn một cách hiệu quả, nhưng đó không phải lúc nào cũng là phương án tốt nhất. Vì vậy, việc hiểu rõ cách hoạt động của từng phương thức eager loading trong ActiveRecord sẽ giúp bạn có được phương án tối ưu nhất cho các bài toán thực tế.

preload

Phương thức đầu tiên mà chúng ta nhắc đến là preload, hãy cùng tìm hiểu nó qua ví dụ sau:

User.preload(posts: :comments)
SELECT `users`.* FROM `users`
SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1, 21, 31, 91, 111, 119, 129)
SELECT `comments`.* FROM `comments` WHERE `comments`.`post_id` IN (1, 11, 21, 31, 41, 51, 61, 71, 81, 91, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 201, 211, 231, 241, 251, 261, 271, 281, 291, 311, 331, 341, 351, 361, 371, 401, 411, 431, 439, 449)

Với preload, ActiveRecord sẽ load dữ liệu thông qua các câu query riêng lẻ:

  • Câu query đầu tiên để lấy ra users
  • Câu query thứ 2 lấy ra posts của các users tương ứng
  • Câu query cuối cùng sẽ lấy tất cả comments trong từng posts

includes

Cũng với ví dụ trên nhưng chúng ta sẽ dùng includes:

User.includes(posts: :comments)
SELECT `users`.* FROM `users`
SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1, 21, 31, 91, 111, 119, 129)
SELECT `comments`.* FROM `comments` WHERE `comments`.`post_id` IN (1, 11, 21, 31, 41, 51, 61, 71, 81, 91, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 201, 211, 231, 241, 251, 261, 271, 281, 291, 311, 331, 341, 351, 361, 371, 401, 411, 431, 439, 449)

Có vẻ như không gì khác so với khi dùng preload, vậy tại sao ActiveRecord lại tạo ra 2 methods này? Hãy cùng xem ví dụ dưới đây để thấy được sự khác biệt:

User.includes(posts: :comments).where(posts: {id: 1})
SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`avatar` AS t0_r2, `users`.`email` AS t0_r3, `users`.`encrypted_password` AS t0_r4, `users`.`address` AS t0_r5, `users`.`phone` AS t0_r6, `users`.`memo` AS t0_r7, `posts`.`id` AS t1_r0, `posts`.`title` AS t1_r1, `posts`.`content` AS t1_r2, `posts`.`thumbnail` AS t1_r3, `posts`.`views` AS t1_r4, `posts`.`point` AS t1_r5, `posts`.`user_id` AS t1_r6, `posts`.`serial_id` AS t1_r7, `posts`.`created_at` AS t1_r8, `posts`.`updated_at` AS t1_r9, `posts`.`status` AS t1_r10, `posts`.`description` AS t1_r11, `posts`.`slug` AS t1_r12, `comments`.`id` AS t2_r0, `comments`.`content` AS t2_r1, `comments`.`user_id` AS t2_r2, `comments`.`post_id` AS t2_r3, `comments`.`created_at` AS t2_r4, `comments`.`updated_at` AS t2_r5 FROM `users` LEFT OUTER JOIN `posts` ON `posts`.`user_id` = `users`.`id` LEFT OUTER JOIN `comments` ON `comments`.`post_id` = `posts`.`id` WHERE `posts`.`id` = 1

Ở trên chúng ta sử dụng includes kết hợp với điều kiện ở bảng quan hệ (posts). Mọi thứ hoạt động bình thường nhưng câu query không còn như trước. Thay vì load dữ liệu ở từng bảng, ActiveRecord đã sử dụng LEFT JOIN để lấy tất cả dữ liệu bằng một câu query duy nhất.

Bây giờ hãy viết lại ví dụ trên nhưng sử dụng preload:

User.preload(posts: :comments).where(posts: {id: 1}).load
User Load (0.9ms) SELECT `users`.* FROM `users` WHERE `posts`.`id` = 1
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'posts.id' in 'where clause'

Mọi thứ không hoạt động và chúng ta sẽ nhận được lỗi như trên. Nguyên nhân là với preload, ActiveRecord sẽ luôn luôn chạy từng câu query riêng để lấy dữ liệu trên từng bảng. Nghĩa là bạn sẽ không thể kết hợp preload với điều kiện khác trên các bảng quan hệ.

eager_load

Bây giờ hãy cùng xem cách mà eager_load làm việc:

User.eager_load(posts: :comments)
SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`avatar` AS t0_r2, `users`.`email` AS t0_r3, `users`.`encrypted_password` AS t0_r4, `users`.`address` AS t0_r5, `users`.`phone` AS t0_r6, `users`.`memo` AS t0_r7, `posts`.`id` AS t1_r0, `posts`.`title` AS t1_r1, `posts`.`content` AS t1_r2, `posts`.`thumbnail` AS t1_r3, `posts`.`views` AS t1_r4, `posts`.`point` AS t1_r5, `posts`.`user_id` AS t1_r6, `posts`.`serial_id` AS t1_r7, `posts`.`created_at` AS t1_r8, `posts`.`updated_at` AS t1_r9, `posts`.`status` AS t1_r10, `posts`.`description` AS t1_r11, `posts`.`slug` AS t1_r12, `comments`.`id` AS t2_r0, `comments`.`content` AS t2_r1, `comments`.`user_id` AS t2_r2, `comments`.`post_id` AS t2_r3, `comments`.`created_at` AS t2_r4, `comments`.`updated_at` AS t2_r5 FROM `users` LEFT OUTER JOIN `posts` ON `posts`.`user_id` = `users`.`id` LEFT OUTER JOIN `comments` ON `comments`.`post_id` = `posts`.`id`

Bạn có thể thấy, eager_load sẽ chỉ dùng một câu query duy nhất để lấy tất cả dữ liệu, bất kể là có thêm điều kiện trên các bảng quan hệ hay không:

User.eager_load(posts: :comments).where(posts: {id: 1})
SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`avatar` AS t0_r2, `users`.`email` AS t0_r3, `users`.`encrypted_password` AS t0_r4, `users`.`address` AS t0_r5, `users`.`phone` AS t0_r6, `users`.`memo` AS t0_r7, `posts`.`id` AS t1_r0, `posts`.`title` AS t1_r1, `posts`.`content` AS t1_r2, `posts`.`thumbnail` AS t1_r3, `posts`.`views` AS t1_r4, `posts`.`point` AS t1_r5, `posts`.`user_id` AS t1_r6, `posts`.`serial_id` AS t1_r7, `posts`.`created_at` AS t1_r8, `posts`.`updated_at` AS t1_r9, `posts`.`status` AS t1_r10, `posts`.`description` AS t1_r11, `posts`.`slug` AS t1_r12, `comments`.`id` AS t2_r0, `comments`.`content` AS t2_r1, `comments`.`user_id` AS t2_r2, `comments`.`post_id` AS t2_r3, `comments`.`created_at` AS t2_r4, `comments`.`updated_at` AS t2_r5 FROM `users` LEFT OUTER JOIN `posts` ON `posts`.`user_id` = `users`.`id` LEFT OUTER JOIN `comments` ON `comments`.`post_id` = `posts`.`id` WHERE `posts`.`id` = 1

Câu query lúc này giống hoàn toàn so với trường hợp sử dụng includes trong ví dụ ở trên.

Như vậy có thể khẳng định, eager_load chính là includes khi kết hợp thêm điều kiện trên các bảng quan hệ. Nhưng chúng vẫn có một khác biệt nhỏ, hãy xem qua ví dụ sau:

User.eager_load(posts: :comments).where("`posts`.`id` = 1")

Mọi thứ hoạt động bình thường, câu query không có gì thay đổi, tuy nhiên với includes:

User.includes(posts: :comments).where("`posts`.`id` = 1")

Chúng ta sẽ chỉ nhận được lỗi tương tự như trường hợp sử dụng preload ở trên:

User Load (12.2ms) SELECT `users`.* FROM `users` WHERE (`posts`.`id` = 1)
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'posts.id' in 'where clause'

Nguyên nhân là với các điều kiện được viết bằng raw query, ActiveRecord sẽ không thể biết được sẽ phải join đến bảng nào để lấy dữ liệu. Lúc này chúng ta phải chỉ định rõ thông qua references:

User.includes(posts: :comments).references(:posts).where("`posts`.`id` = 1")

Bây giờ mọi thứ sẽ lại hoạt động bình thường và includes lại biến thành eager_load

Summary

Qua các ví dụ bên trên, chắc hẳn bạn cũng đã phần nào hiểu được cách hoạt động cũng như điểm giống và khác nhau giữa các phương thức eager loading trong ActiveRecord. Đây là những kiến thức cần thiết bạn cần nắm được trước khi quyết định sẽ lựa chọn phương thức cho các bài toàn cụ thể. Ở phần sau, chúng ta sẽ đi tìm hiểu về chủ đề này.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tôi cá là bạn không biết những điều này - Ruby on rails ( Phần 2)

Các bạn có thể theo dõi phần 1 ở đây :. https://viblo.asia/p/toi-ca-la-ban-khong-biet-nhung-dieu-nay-ruby-on-rails-phan-1-WAyK8DDeKxX. 5.

0 0 222

- vừa được xem lúc

Rails Memoization

Memoization is a process that can be used to speed up rails methods. It caches the results of methods that do time-consuming work, work that only needs to be done once. Here is an example. Example.

0 0 48

- vừa được xem lúc

Tại sao Rails lại dùng cả Webpack lẫn Sprocket?

Khi Rails 6 được ra mắt, có thể bạn đã từng tự hỏi. WTF, sao Webpack đã được add vào rồi, mà Sprocket vẫn tồn tại thế kia . Chẳng phải Webpack và Sprocket được dùng để giải quyết chung một công việc hay sao. Hoặc cả đây:.

0 0 59

- vừa được xem lúc

Bạn nên sử dụng Elasticsearch trong ứng dụng Ruby On Rails như thế nào?

Elasticsearch là một công cụ phân tích và mã nguồn mở mạnh mẽ và linh hoạt, phân tán, theo thời gian thực. Đó là tiêu chuẩn vàng trong công nghệ tìm kiếm.

0 0 80

- vừa được xem lúc

Form object pattern trong rails

1.Mở đầu.

0 0 111

- vừa được xem lúc

Sử dụng Twilio để gửi SMS trong ứng dụng Ruby on Rails

Ngoài cách xác nhận tài khoản hay gửi thông báo bằng email thì hôm nay mình sẽ hướng dẫn các bạn 1 cách nữa là thông qua SMS/Voice. Công cụ sử dụng sẽ là gem Twilio. Installation. Để cài đặt bằng Bundler, hãy lấy phiên bản mới nhất:.

0 0 67