Phân biệt size, length, count trong rails
count, size, length đều dùng để tính số lượng. Vậy tại sao có đến tận 3 method?. Chúng ta hãy cùng nhau tìm hiểu qua bài viết này. Trước hết ta có bối cảnh như sau: Bảng users lưu trữ các thông tin về các users Bảng comments lưu các thông tin về các comments của mỗi User. Một user có ...
count, size, length đều dùng để tính số lượng. Vậy tại sao có đến tận 3 method?.
Chúng ta hãy cùng nhau tìm hiểu qua bài viết này.
Trước hết ta có bối cảnh như sau:
Bảng users lưu trữ các thông tin về các users
Bảng comments lưu các thông tin về các comments của mỗi User.
Một user có nhiều comments.
class User has_many :comments end class Comment belongs_to :user, counter_cache: true end
Counter cache là gì? Các bạn có thể tham khảo bài viết Counter Cache
Chú ý đừng nhầm lẫn với từ cache (lưu vào bộ nhớ tạm) trong bài viết này sử dụng.
A. count
Count có vẻ như được biết tới nhiều hơn. Hãy cùng xem count hoạt động như thế nào
User.count (0.3ms) SELECT COUNT(*) FROM "users" => 52 users = User.all users.count (0.3ms) SELECT COUNT(*) FROM "users" => 52
=> Không có gì bất ngờ, count đã vào database để đếm số lượng bản ghi của scope hiện tại.
B. length
Hãy xem length thực hiện như thế nào...
users = User.all users.length User Load (1.3ms) SELECT "users".* FROM "users" => 52 users.length => 52 User.length undefined method `length' for #<Class:0x00000005219238>
=> Như vậy rails đã vào database để load tất cả các users về và đo kích thước của mảng kết quả. Thông thường cách này sẽ lâu hơn việt sử dụng User.all.count
Và cũng bởi ý nghĩa như vậy nên không có phương thức length cho class User.
Sử dụng length có vẻ là cách ngu đần hơn khi muốn đếm số phần tử mà lại đi load database về và đếm nhưng hãy để ý.
Khi gọi users.length lần thứ 2 chúng ta có kết quả 52 vì length sẽ đếm số phần tử hiện tại của mảng.
=> vậy là nó cũng có ưu điểm nhất định.
C. size
Vậy còn size?
User.all.size (0.3ms) SELECT COUNT(*) FROM "users" => 52 users = User.all users.size => 52 users.size => 52 User.size undefined method `size' for #<Class:0x00000005219238>
=> Vậy là size sẽ vào database gọi đến SQL SELECT COUNT(*) khi chưa có cache (tức là khi chưa có các bản ghi users trên bộ nhớ tạm), khi đã có rồi thì đếm luôn số bản ghi trên bộ nhớ tạm.
Vậy là size là giải pháp thông minh nhất và nên dùng nhất.
Tuy nhiên, không thể hoàn toàn thay thế count bằng size được.
Vì kết quả của length mang ý nghĩa là số phần tử trên mảng, của count mang ý nghĩa là số phần tử trong database.
Size sẽ chọn cách nào tốt nhất để lấy ra kết quả nhanh nhất.
A.count
User.first.comments.count (0.2ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."user_id" = ? [["user_id", 1]]
=> Không có gì bất ngờ, count vẫn sẽ sử dụng query database để lấy số comments của user 1
B.length
User.first.comments.length SELECT "comments".* FROM "comments" WHERE "comments"."user_id" = ? [["user_id", 1]]
=> vẫn ngu như ngày nào, length vẫn query vào database để load các comments về và đếm độ dài mảng. Và sẽ đếm trực tiếp nếu như đã có cache.
C.size
Vậy còn người bạn thông minh sáng sủa của chúng ta, size ?
User.first.comments.size SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> lạ nhỉ, tưởng sẽ phải là SELECT COUNT(*) chứ?, mà sao lại SELECT từ users.
=> à, cùng nhớ lại, chúng ta đang sử dụng, counter cache, số comments của user đã được lưu ở cột comments_count nên không cần phải động chạm gì đến bảng comments nữa.
=> thông minh lần 2! size sẽ tự chọn giữa COUNT(*), đếm trên cache, hoặc đọc trong counter_cache.
Vì vậy trong hầu hết các trường hợp nên dùng size.
Vì vậy trong hầu hết các trường hợp nên dùng size !.
Hi vọng các bạn thấy bài viết này hữu ích.
Tham khảo: https://www.reddit.com/r/rails/comments/3oh61p/counter_cache_sizecountlength/