Help us understand the problem. What is going on with this article?

find、find_by、whereの違い

More than 1 year has passed since last update.

しっかりと、どう違うという説明ができなかったので、まとめました。

findメソッドとは

各モデルのidを検索キーとしてデータを取得するメソッド
id以外の条件で検索不可
取得したいデータのidの値が、1、10と具体的に分かっている場合に使用する。

(例)Aiueoモデルからid=1のデータを取得したい場合

irb(main):001:0> Aiueo.find(1)

  Aiueo Load (0.9ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
=> #<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">

該当データがない場合は、ActiveRecord::RecordNotFoundが返ってくる。
つまり、エラーが返ってくるということ。

irb(main):006:0> Aiueo.find(1000000)
  Aiueo Load (0.4ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."id" = $1 LIMIT $2  [["id", 1000000], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find Aiueo with 'id'=1000000

find_byメソッドとは

各モデルをid以外の条件で検索するメソッド(idでも検索可能)
複数の検索条件を指定可能
返ってくる結果は、最初にヒットした1件のみ
id及びid以外の条件が分かっている場合、その条件に該当する最初のデータを取得したい場合に使用する。

(例)Aiueoモデルからタイトルが「タイトル0」のデータを取得する場合

irb(main):004:0> Aiueo.find_by(title: "タイトル0")
  Aiueo Load (0.4ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 1]]
=> #<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">

該当データがない場合は、nilが返ってくる。

irb(main):007:0> Aiueo.find_by(title: "タイトル")
  Aiueo Load (0.6ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル"], ["LIMIT", 1]]
=> nil

whereメソッド

各モデルをid以外の条件で検索する場合
該当するデータ全てが返ってくる。

(例)Aiueoモデルからタイトルが「タイトル0」のデータを取得する場合

irb(main):005:0> Aiueo.where(title: "タイトル0")
  Aiueo Load (1.0ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">]>

find_byの結果と違い、先頭にActiveRecord::Relationの文字が。
ActiveRecord::Relation は、検索クエリを組み立てるもので、検索結果のオブジェクトとは異なるものである、とのこと。

う〜ん、むずかしい・・・

findfind_byメソッドは、検索結果のインスタンス(オブジェクト)(簡単にいうと、検索結果のデータがつまったもの)を返すが、
whereメソッドは、検索結果をオブジェクトとして返しているのではなく、検索するためのプログラムを発生させ、結果を表示しているだけの状態(うまく説明できない・・・)

なので、whereでは、取得したデータから特定カラムのデータを取得しようとするとエラーになる。

irb(main):012:0> @aiueo = Aiueo.where(title: "タイトル0")
  Aiueo Load (0.6ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">]>

irb(main):013:0> @aiueo.title
NoMethodError:   Aiueo Load (0.4ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 11]]
undefined method `title' for #<Aiueo::ActiveRecord_Relation:0x007fef48b8c3a8>

一方、find_byでは、実行結果のインスタンスが返ってきているので、特定カラムのデータも取得できる。

irb(main):014:0> @aiueo = Aiueo.find_by(title: "タイトル0")
  Aiueo Load (0.9ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 1]]
=> #<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">

irb(main):015:0> @aiueo.title
=> "タイトル0"

まとめ

使用区分としては、

idの値が分かっていて、そのidのデータを取得したい場合・・・find
idの値が不明で、id以外のカラムを検索条件としたい場合・・・find_by
id以外のカラムの検索条件で、複数の実行結果を取得したい場合・・・where

こんなところかなと。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away