元祖若手のプログラミング奮闘記

元祖若手の奮闘記。主にメモ

Rails5.2とbelongs_toとN+1問題の話

結果
レコードに登録するときに
外部キーをidとして入れると
N+1になる

環境はこちら

rails 5.2.1
activerecord-import 0.27.0
ruby 2.5.3

なにいってんだこいつ的な感じだと思うので説明

class User < ApplicationRecord
  has_many :items
end

class Item < ApplicationRecord
  belongs_to :user
  validates :name, presence: true
end

そしてItemを複数取り込みする処理があるとする

class ItemImport
  def execute
    items = User.all.limit(100).each_with_object([]) do |user, items|
      %w(item_a item_b item_c).each do |name|
        item = Item.new(user_id: user.id, name: name)
        next unless item.valid?
        items << item
      end
    end

    Items.import items
  end
end

ここでN+1発生

>> ItemImport.new.execute
(前略)
User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.1ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.1ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.2ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
User Load (1.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
User Load (0.1ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
(後略)

Rails5からbelongs_toで指定した値は
入力必須のため外部IDだけ入れておくと
ちゃんと有効な値か存在チェックをしてくれるという
仕様からうまれた問題だった

item = Item.new(user: user, name: name)

とすることで
チェック不要の有効な値が入っていると認識してくれて
無駄なクエリを発行せず
Insertしてくれるのでした
おしまい!

Railsって結構オンラインプログラミングスクールの
教材として使われるけど
完全にオーバースペックだよね
っていう小話