🐘

【Rails】present? exists? presence の違い

2024/03/27に公開

概要

「条件に合致するレコードが存在するかどうか」というコードを書くことはよくあるはず。

そんなとき present? を使用するのか exists? を使用するのか、はたまた presence を使用するのかという選択肢がある。
それぞれどんな感じで動いているのか、どんなときに使うのかというのを書いていく。

環境

rails (7.0.8)
RUBY VERSION
   ruby 3.1.2p20

present?

rails console
> Article.where(title: "あああ").present?
SELECT `articles`.* FROM `articles` WHERE `articles`.`title` = 'あああ'
=> true

present?は SQL で データベースから title が「あああ」である全レコードを取得し、
Rails(ActiveSupport) のコードで存在を確認している。

exists?

rails console
> Article.where(title: "あああ").exists?
SELECT 1 AS one FROM `articles` WHERE `articles`.`title` = 'あああ' LIMIT 1
=> true

exist?は title が「あああ」のレコードを1件だけ取得し、「1」という固定値を返している。
present?のようにレコードを全件取得せず、存在の有無だけを確認している。
レコード有無のチェックでいうと、present? よりもパフォーマンスが良い。

presence

rails console
> Article.where(title: "あああ").presence
SELECT `articles`.* FROM `articles` WHERE `articles`.`title` = 'あああ'
=> [#<Article:0x0000ffffae21c470 id: 1, title: "あああ", body: "body", is_published: false, created_at: Mon, 26 Feb 2024 00:52:53.492071000 UTC +00:00, updated_at: Mon, 26 Feb 2024 00:52:53.492546000 UTC +00:00>]
rails console
> Article.where(title: "ををを").presence
Article Load (6.3ms)  SELECT `articles`.* FROM `articles` WHERE `articles`.`title` = 'ををを'
=> nil

presenceは発行されるクエリはpresent?と同じですが、戻り値として、該当するデータセットを返す。
該当するレコードが存在しない場合、nil を返す。

レコードの存在確認ではなく、「値が存在すればその値を使い、なければ別の値を使う」のようなコードを書きたいときに便利。

> user.name || "名前がありません"
=> "たなか"

> user.name = nil
> user.name || "名前がありません"
=> "名前がありません"

まとめ

SQL 戻り値 ユースケース
present? 条件に当てはまるコードを全件取得。その後あるかどうか true / false 単純な Ruby のコードでの存在確認
exists? 条件に当てはまるコードが存在するかどうか true / false テーブルに条件に一致するレコードの存在確認
presence present? と同じ 該当するデータセット / nil 値が存在すればその値を使う。なければ別の値を使う

Discussion