Posted at

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

こんなところかなと。