◦ `all` and `where` do not execute SQL, but `to_a` does. ◦ It is delayed until when the records are necessary • Basically, RDBMS should do it if RDBMS can.
be faster if it's called several times ◦ Because Active Record caches the result to `@records` # A articles = Article.all x = articles.select { _1.is_active } # Article Load (3.1ms) SELECT `articles`.* FROM `articles` y = articles.select { not _1.is_active } # nothing # B x = Article .where(is_active: true) .to_a # Article Load (1.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`is_active` = TRUE y = Article .where(is_active: false) .to_a # Article Load (1.9ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`is_active` = FALSE Note: Enumerable#partition is more appropriate.
the debug log • Use `AR::Relation#to_sql` method ◦ irb(main):001> Article.all.where(blog_id: 42).to_sql => "SELECT `articles`.* FROM `articles` WHERE `articles`.`blog_id` = 42"
Exists? (0.9ms) SELECT 1 AS one FROM `articles` WHERE `articles`.`blog_id` = 42 LIMIT 1 # B Article.all .any? { _1.blog_id == 42 } # Article Load (1.7ms) SELECT `articles`.* FROM `articles` 💯 A is faster than B!
Active Record has specific methods such as exists?. ◦ Use them instead of Enumerable's method • Enumerable methods are useful, but it is not optimized for AR. ◦ You can easily distinguish by that the method takes a block yielding an AR object ◦ But…
{ … }` needs records to execute the block • On the other hand, it does not need records if it does not execute the block • We have `RubyVM::AbstractSyntaxTree.of` to solve this problem!
I will talk about RBS. ◦ At Matsue (registration closed😢) and Online • Nov. 16: 各社の技術広報が明かす「RubyKaigiスポンサーの裏話」運営ノウハウ やコミュニティへの想い https://moneyforward.connpass.com/event/298325/ ◦ Acha-san will talk about sponsoring RubyKaigi ◦ Online event • We have an X account @moneyforwardDev!
event to watch RubyKaigi 2023 videos. • In a zoom meeting. • At 12:10-12:40 on every Thursday • Accounced in #rubykaigi channel of ruby-jp slack. • We'll watch "RubyGems on the watch / Maciej Mensfeld" as the next session in 2nd Nov. • Feel free to join us!
can_update = current_user .permissions .exists?(action: :update) # Permission Exists? (0.8ms) SELECT 1 AS one FROM `permissions` WHERE `permissions`.`user_id` = 42 AND `permissions`.`action` = 'update' response = Article.all.map do { title: _1.title, can_update:, } end # B current_user = User.find(42) response = Article.all.map do can_update = current_user .permissions .exists?(action: :update) # Permission Exists? (0.8ms) SELECT 1 AS one FROM `permissions` WHERE `permissions`.`user_id` = 42 AND `permissions`.`action` = 'update' { title: _1.title, can_update:, } end 💯 A is faster than B!
the difference. We can confirm it with `ActiveRecord.cache do -- end` It is enabled by default in controller actions, but disabled in other places, such as job, rails console.
response = Article.all.map do { title: _1.title, categories:, } end # B response = Article.all.map do categories = Category.all.to_a { title: _1.title, categories:, } end
response = Article.all.map do { title: _1.title, categories:, } end # B response = Article.all.map do categories = Category.all.to_a { title: _1.title, categories:, } end 💯 A is faster than B!
AR cache needs to cache boolean value for `exists?` • But it needs to cache AR model objects for query fetching objects ◦ Initialization AR model is heavy