はじめに
初学者の頃から分かったような気がするけど、適切に使いこなせてなかった
ActiveRecord
の exists?
, present?
, empty?
, blank?
の
使い分けについて最近やっと腑に落ちたのでメモリます。
exists
データが1件でもあるかどうかチェックするにはModel.exists?
を使う。
どういうことかというと
Pokemon.exists?
Pokemon Exists? (98.6ms) SELECT 1 AS one FROM `pokemon` LIMIT 1
=> true
ポイントになるのがSELECT 1 AS
というところ。
データが1行以上「あるかどうか」だけが問題となります。
ということで、データの値を取ってくる必要がないため、
カラムの情報が一切ないことがわかります。
ちなみにこれと反対の意味を持つのがempty?
である
blank?
Pokemon.where(type: 'FIRE').blank?
Pokemon Load (481.6ms) SELECT `pokemon`.* FROM `pokemon` WHERE `pokemon`.`type` = 'FIRE'
=> true
ポイントはSELECT pokemon.*
の部分で、条件にマッチするデータを全て、カラム情報込み で取得していることがわかる。
ちなみにこれと反対の意味を持つのがpresent?
である
では、 どんな時に使い分けると嬉しいか
ある特定の条件を満たすデータがデータベースに存在するかどうかを確認する場合に、exists?
が適しています。(存在しないことを確認したいなら empty?
)
これはデータの有無だけが気になる場合で、具体的なデータの値を使用しないときです。
if pokemon.exists?(type: 'Fire')
puts "炎タイプのポケモンが登録されていません。"
else
puts "炎タイプのポケモンは登録されています。"
end
一方で、present? / blank?
は特定の条件を満たすデータがデータベースに存在するかだけでなく、それらのデータの値も利用する場合 に使いたい。
fire_pokemon = Pokemon.where(type: 'FIRE')
return if fire_pokemon.blank? # この時点で必要なデータも取得できている
puts "Fire タイプのポケモンが存在します。"
# blank?で取得したデータを利用するので、発行されるSQLは1件のみ
puts "それらのポケモン: #{fire_pokemon.pluck(:name).join(', ')}"
仮に、データの値も利用する場合に empty?
を使ってしまうと、後でデータを使用する時に、改めてデータを引いてしまうので、DBサーバーにアクセスする回数が2回になってしまう。
fire_pokemon = Pokemon.where(type: 'FIRE')
return if fire_pokemon.empty? # データが存在するどうかを確認するためにDBにアクセス
# 利用するデータを引くためにDBに再アクセスする必要がある
puts "それらのポケモン: #{fire_pokemon.pluck(:name).join(', ')}"
一概にデータの確認の際にはempty?
と決めに行くのではなく、
処理全体の内容によってempty? / blank?, exists? / present?
を
使い分けるようにしたい。
最後に
ここまで見てくださりありがとうございます。m(_ _)m
この手の記事は山ほどありますが、自身の勉強視点でまとめてみた感じです。
誰かの役に立てば嬉しいです。