empty?, blank?, any?, exists? - Cách hoạt động và tốc độ của chúng
Ruby on Rails ActiveRecord cung cấp một số phương thức để kiểm tra xem một quan hệ trả về 0 hoặc nhiều bản ghi. empty? and blank? trả về true nếu một quan hệ trả về 0 bản ghi. any? and exists? trả true nếu một quan hệ trả về ít nhất một bản ghi. empty? và blank? Chúng ta cùng tìm hiểu kỹ ...
Ruby on Rails ActiveRecord cung cấp một số phương thức để kiểm tra xem một quan hệ trả về 0 hoặc nhiều bản ghi.
- empty? and blank? trả về true nếu một quan hệ trả về 0 bản ghi.
- any? and exists? trả true nếu một quan hệ trả về ít nhất một bản ghi.
empty? và blank?
Chúng ta cùng tìm hiểu kỹ hơn hai phương thức empty? và blank? để hiểu rõ hơn cách chúng hoạt động và khi nào nên sử dụng chúng. Tài liệu của RoR cho biết rằng blank? trả về true nếu quan hệ là rỗng và empty? trả true nếu như không có bản ghi nào được trả về. Chúng ta cùng kiểm tra các truy vấn được sinh ra trong Rails console để hiểu chính xác cách thức chúng hoạt động.
User.where(admin: true).empty? (0.6ms) SELECT COUNT(*) FROM `users` WHERE `users`.`admin` = 1 => false User.where(admin: true).blank? User Load (0.9ms) SELECT `users`.* FROM `users` WHERE `users`.`admin` = 1 => false
Trong ví dụ trên, empty? chỉ lấy ra số lượng các bản ghi đáp ứng được điều kiện cụ thể, trong khi blank? lấy ra tất cả các bản ghi đó. Điều đó có nghĩa là empty? sẽ chạy nhanh hơn blank?.
Cùng với truy vấn trên, nhưng điều gì sẽ xảy ra nếu như các quan hệ đã được tải trước.
admins = User.where(admin: true).load User Load (1.0ms) SELECT `users`.* FROM `users` WHERE `users`.`admin` = 1 admin.blank? => false admin.empty? => false
Lúc này, cả hai phương thức đều dựa vào các bản ghi đã được tải trước chứ không phải truy vấn từ cơ sở dữ liệu ra. Như vậy cả hai đều có hiệu năng như nhau.
Như vậy ta rút ra kết luận: để kiểm tra một quan hệ có rỗng hay không, empty? sẽ có hiệu năng tốt hơn nếu như quan hệ đó chưa được tải trước và chúng sẽ có hiệu năng như nhau nếu quan hệ đó được tải trước.
any? và exists?
Hai phương thức này sử dụng các cách khác nhau để kiểm tra xem có bản ghi nào được trả về hay không.
any? nhận một khối lệnh như một tham số. Nó lấy ra những bản ghi trong quan hệ đang xét (trừ khi chúng đã được tải lên trước), biểu diễn chúng dưới dạng một mảng sau đó gọi phương thức Enumerable#any? với mảng đó. Nếu không truyền vào tham số, phương thức này tương đương với việc thực thi phương thức !empty? và lấy ra số lượng những bản ghi trong quan hệ từ cơ sở dữ liệu hoặc dựa trên dữ liệu đã được tải lên trước.
exists? luôn luôn truy vấn từ cơ sở dữ liệu và không bao giờ dựa trên những bản ghi được tải lên trước.
User.where(admin: true).exists? `User Exists (1.9ms) SELECT 1 AS one FROM `users` WHERE `users`.`admin` = 1 LIMIT 1`
Nó chỉ lấy ra 1 bản ghi, như vậy nó sẽ nhanh hơn khi so sánh với phương thức any? khi không có khối lệnh đi kèm. exists? cũng nhận vào những tham số khác nhau và sử dụng chúng để truy vấn cơ sở dữ liệu.
Nếu như phương thức any? đi cùng một khối lệnh và dữ liệu chưa được tải lên trước thì exists? sẽ có hiệu năng tốt hơn. Cùng xét qua ví dụ sau để hiểu rõ hơn:
User.any? { |u| u.admin? } User Load (2.7ms) SELECT `users`.* FROM `users` User.exists?(admin: true) User Exists (0.4ms) SELECT 1 AS one FROM `users` WHERE `users`.`admin` = 1 LIMIT 1
Kết luận
Nhìn lại bốn phương thức trên để tìm ra cách có tốc độ nhanh nhất, với ví dụ, trong bảng users không có bản ghi nào mà cột admin có giá trị là true.
User.where(admin: true).blank? sẽ lấy ra tất cả các bản ghi mà cột admin nhận có giá trị true và đếm số lượng chúng.
User.where(admin: true).empty? sẽ lấy ra số lượng các bản ghi mà cột admin có giá trị true.
!User.where(admin: true).any?hoạt động giống như trên.
!User.any? { |u| u.admin? } sẽ lấy ra tất cả các bản ghi từ bảng users và sau đó kiểm tra xem có ít nhất một trong số chúng có giá trị admin? là true.
!User.exists?(admin: true) or !User.where(admin: true).exists? sẽ lấy ra bản ghi đầu tiên thoả mãn admin là true. Dễ dàng nhận thấy đây là cách nhanh nhất trong số 5 cách được xem xét.
Tham khảo bài viết tại: http://andreigridnev.com/empty-blank-any-exists-methods-of-ruby-on-rails-activerecord/