LoginSignup
2
1

More than 1 year has passed since last update.

【Rails】present?とexists?の挙動を確認し、使い分けの基準を整理する

Posted at

1. present?

  • Railsのメソッド
  • オブジェクトがある場合はtrueを返す
  • present?の反対は、blank?
  • 実行するとキャッシュされる

発行されるクエリを見てみると、1回全部の該当レコードをごそっと取ってきていることが分かる
SELECT "articles".* FROM "articles" WHERE "articles"."title" = ? [["title", "Test"]]

irb(main):001:0> Article.where(title: 'Test').present?
   (1.6ms)  SELECT sqlite_version(*)
  Article Load (0.2ms)  SELECT "articles".* FROM "articles" WHERE "articles"."title" = ?  [["title", "Test"]]
=> true

2. exists?

  • Railsのメソッド
  • SQLのexists式を使う
    • 該当レコードが1件でもあればtrueを返す

発行されるクエリを見てみると、該当するレコードをLIMIT 1で絞り込んでいることが分かる
SELECT 1 AS one FROM "articles" WHERE "articles"."title" = ? LIMIT ? [["title", "Test"], ["LIMIT", 1]]

irb(main):002:0> Article.where(title: 'Test').exists?
  Article Exists? (0.2ms)  SELECT 1 AS one FROM "articles" WHERE "articles"."title" = ? LIMIT ?  [["title", "Test"], ["LIMIT", 1]]
=> true

3. exists?を使うべき時

  • 該当のレコード数が何百万件と多い時
    • present?の場合該当する全レコードを取得するため、データが多い場合そこでパフォーマンスの問題が生じる可能性が高い
  • キャッシュを使う必要がない時
if Article.where(title: 'Test').exists?
 p 'タイトルがTestという記事が存在します!'
else
 p 'タイトルがTestという記事は存在しません'
end

Article Exists? (0.2ms)  SELECT 1 AS one FROM "articles" WHERE "articles"."title" = ? LIMIT ?  [["title", "Test"], ["LIMIT", 1]]
"タイトルがTestという記事が存在します!"
=> "タイトルがTestという記事が存在します!"

4. present?を使うべき時

  • 存在確認後に、キャッシュを使う予定がある時
  • 既にキャッシュが存在していて、それが使える時

articles.present?Article.where(id: 1)の結果がキャッシュされ、articles.pluck(:title)実行時にはキャッシュを使うため、クエリは1回しか発行されない

articles = Article.where(id: 1)
if articles.present?
 p "タイトルは#{articles.pluck(:title)}です"
else
 p '該当する記事がありません'
end

Article Load (0.2ms)  SELECT "articles".* FROM "articles" WHERE "articles"."id" = ?  [["id", 1]]
"タイトルは[\"Test\"]です"
=> "タイトルは[\"Test\"]です

exists?を使うと、クエリが2回発行されてしまう

if articles.exists?
 p "タイトルは#{articles.pluck(:title)}です"
else
 p '該当する記事がありません'
end
  
Article Exists? (0.2ms)  SELECT 1 AS one FROM "articles" WHERE "articles"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 (0.1ms)  SELECT "articles"."title" FROM "articles" WHERE "articles"."id" = ?  [["id", 1]]
"タイトルは[\"Test\"]です"
=> "タイトルは[\"Test\"]です"

5. まとめ

present?とexists?を使う基準は以下の通り

  • exists?:キャッシュを使わない、該当のレコード数が何百万件と多い時
  • present?:存在確認後にキャッシュを使う、存在確認前に既にキャッシュがある時

参考

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1