Railsにてfind, find_by, whereの違いについて

## はじめに
Railsでモデルの検索に使えるメソッド`find, find_by, where`の使い方や違いについて紹介します。
## 結論
||find|find_by|where|
|------------|---------|---------|------------|
|括弧の中に指定するものは?|id(複数可能)|条件|条件
|戻り値|条件に一致するデータ全て|条件に一致する最初の1件だけ|条件に一致するデータ全て
|戻り値の型|モデルのインスタンス　(結果が複数の場合は配列)|モデルのインスタンス|`ActiveRecord::Relation`のインスタンス
|検索結果がない場合|例外発生|`nil`を返す|空きの`ActiveRecord::Relation`を返す
## 動作確認
### テストデータ
```
irb(main):015:0> Word.all
  Word Load (4.5ms)  SELECT `words`.* FROM `words`
=>
[#<Word:0x00007f21a3d7ed78
  id: 1,
  vocabulary: "詐欺",
  pronunciation: "さぎ",
  viewing: 10,
  meaning: "相手をだますこと",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00>,
 #<Word:0x00007f21a3d7e968
  id: 2,
  vocabulary: "鷺",
  pronunciation: "さぎ",
  viewing: 53,
  meaning: "鳥の一種類",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.278526000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.278526000 UTC +00:00>,
 #<Word:0x00007f21a3d7e800
  id: 3,
  vocabulary: "子供",
  pronunciation: "こども",
  viewing: 11,
  meaning: "年のいかない幼い者",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00>,
 #<Word:0x00007f21a3d7e698
  id: 4,
  vocabulary: "大人",
  pronunciation: "おとな",
  viewing: 20,
  meaning: "成長して一人前になった人",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.321767000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.321767000 UTC +00:00>]
```
### 括弧の中に指定するものは?
#### find
モデルののIDを指定して検索する。IDは1個または複数を指定可能。
```
★1個指定したとき
irb(main):016:0> Word.find(1)
  Word Load (2.5ms)  SELECT `words`.* FROM `words` WHERE `words`.`id` = 1 LIMIT 1
=>
#<Word:0x00007f21a277bb20
 id: 1,
 vocabulary: "詐欺",
 pronunciation: "さぎ",
 viewing: 10,
 meaning: "相手をだますこと",
 example: nil,
 created_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00,
 updated_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00>

★複数指定したとき
irb(main):017:0> Word.find(1,2)
  Word Load (1.1ms)  SELECT `words`.* FROM `words` WHERE `words`.`id` IN (1, 2)
=>
[#<Word:0x00007f21a26b6c58
  id: 1,
  vocabulary: "詐欺",
  pronunciation: "さぎ",
  viewing: 10,
  meaning: "相手をだますこと",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00>,
 #<Word:0x00007f21a26b6b90
  id: 2,
  vocabulary: "鷺",
  pronunciation: "さぎ",
  viewing: 53,
  meaning: "鳥の一種類",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.278526000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.278526000 UTC +00:00>]

★複数指定したときは配列でもOK
irb(main):021:0> Word.find([1,3,4])
  Word Load (1.0ms)  SELECT `words`.* FROM `words` WHERE `words`.`id` IN (1, 3, 4)
=>
[#<Word:0x00007f21a30829a8
  id: 1,
  vocabulary: "詐欺",
  pronunciation: "さぎ",
  viewing: 10,
  meaning: "相手をだますこと",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00>,
 #<Word:0x00007f21a30828b8
  id: 3,
  vocabulary: "子供",
  pronunciation: "こども",
  viewing: 11,
  meaning: "年のいかない幼い者",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00>,
 #<Word:0x00007f21a30827f0
  id: 4,
  vocabulary: "大人",
  pronunciation: "おとな",
  viewing: 20,
  meaning: "成長して一人前になった人",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.321767000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.321767000 UTC +00:00>]
```
#### find_by
条件に`モデルの属性: 値`の形を指定して検索する。
```
irb(main):024:0> Word.find_by(vocabulary: "鷺")
  Word Load (1.2ms)  SELECT `words`.* FROM `words` WHERE `words`.`vocabulary` = '鷺' LIMIT 1
=>
#<Word:0x00007f21a3da92d0
 id: 2,
 vocabulary: "鷺",
 pronunciation: "さぎ",
 viewing: 53,
 meaning: "鳥の一種類",
 example: nil,
 created_at: Sun, 23 Jul 2023 04:25:33.278526000 UTC +00:00,
 updated_at: Sun, 23 Jul 2023 04:25:33.278526000 UTC +00:00>
```
#### where
SQL文のwhere句に書くような感覚で条件指定して検索をする。
`モデルの属性: 値`とか`モデルの属性 > 値`みたいに
```
★例1
irb(main):031:0> Word.where(vocabulary: "子供")
  Word Load (0.8ms)  SELECT `words`.* FROM `words` WHERE `words`.`vocabulary` = '子供'
=>
[#<Word:0x00007f21a30dc8e0
  id: 3,
  vocabulary: "子供",
  pronunciation: "こども",
  viewing: 11,
  meaning: "年のいかない幼い者",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00>]

★例2
irb(main):036:0> Word.where("id > 2")
  Word Load (1.1ms)  SELECT `words`.* FROM `words` WHERE (id > 2)
=>
[#<Word:0x00007f21a3d9ed30
  id: 3,
  vocabulary: "子供",
  pronunciation: "こども",
  viewing: 11,
  meaning: "年のいかない幼い者",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00>,
 #<Word:0x00007f21a3d9e740
  id: 4,
  vocabulary: "大人",
  pronunciation: "おとな",
  viewing: 20,
  meaning: "成長して一人前になった人",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.321767000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.321767000 UTC +00:00>]

★例3(id=2以外を取得)
irb(main):039:0> Word.where("id <> 2")
  Word Load (1.1ms)  SELECT `words`.* FROM `words` WHERE (id <> 2)
=>
[#<Word:0x00007f21a3c45a10
  id: 1,
  vocabulary: "詐欺",
  pronunciation: "さぎ",
  viewing: 10,
  meaning: "相手をだますこと",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00>,
 #<Word:0x00007f21a3c458d0
  id: 3,
  vocabulary: "子供",
  pronunciation: "こども",
  viewing: 11,
  meaning: "年のいかない幼い者",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.298938000 UTC +00:00>,
 #<Word:0x00007f21a3c45808
  id: 4,
  vocabulary: "大人",
  pronunciation: "おとな",
  viewing: 20,
  meaning: "成長して一人前になった人",
  example: nil,
  created_at: Sun, 23 Jul 2023 04:25:33.321767000 UTC +00:00,
  updated_at: Sun, 23 Jul 2023 04:25:33.321767000 UTC +00:00>]
```
### 戻り値
#### find
(「括弧の中に指定するものは?」と検証内容がかぶるため割愛します。)
#### find_by
```
irb(main):045:0> Word.find_by(pronunciation: "さぎ")
  Word Load (8.4ms)  SELECT `words`.* FROM `words` WHERE `words`.`pronunciation` = 'さぎ' LIMIT 1
=>
#<Word:0x00007f21a308b9b8
 id: 1,
 vocabulary: "詐欺",
 pronunciation: "さぎ",
 viewing: 10,
 meaning: "相手をだますこと",
 example: nil,
 created_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00,
 updated_at: Sun, 23 Jul 2023 04:25:33.251697000 UTC +00:00>
```
`pronunciation: "さぎ"`は2件存在するが、最初の1件だけ返されました。

#### where
(「括弧の中に指定するものは?」と検証内容がかぶるため割愛します。)
### 戻り値の型
#### find
```
★結果が1件の場合、Wordのインスタンスを返す
irb(main):052:0> Word.find(4).class
  Word Load (2.7ms)  SELECT `words`.* FROM `words` WHERE `words`.`id` = 4 LIMIT 1
=> Word(id: integer, vocabulary: string, pronunciation: string, viewing: integer, meaning: text, example: text, created_at: datetime, updated_at: datetime)

★結果が複数の場合、配列を返す
irb(main):053:0> Word.find([2,4]).class
  Word Load (1.4ms)  SELECT `words`.* FROM `words` WHERE `words`.`id` IN (2, 4)
=> Array

★配列を返したとしても中身はWordのインスタンスの集合
irb(main):054:0> Word.find([2,4])[1].class
  Word Load (2.7ms)  SELECT `words`.* FROM `words` WHERE `words`.`id` IN (2, 4)
=> Word(id: integer, vocabulary: string, pronunciation: string, viewing: integer, meaning: text, example: text, created_at: datetime, updated_at: datetime)
```

#### find_by
```
irb(main):058:0> Word.find_by(pronunciation: "さぎ").class
  Word Load (0.9ms)  SELECT `words`.* FROM `words` WHERE `words`.`pronunciation` = 'さぎ' LIMIT 1
=> Word(id: integer, vocabulary: string, pronunciation: string, viewing: integer, meaning: text, example: text, created_at: datetime, updated_at: datetime)
```
Wordのインスタンスを返す
#### where
```
irb(main):066:0> Word.where("id in (2, 4)").class
=> Word::ActiveRecord_Relation
```
### 検索結果がない場合
#### find
```
irb(main):069:0> Word.find(5)
  Word Load (1.1ms)  SELECT `words`.* FROM `words` WHERE `words`.`id` = 5 LIMIT 1
/usr/local/bundle/gems/activerecord-7.0.4.2/lib/active_record/core.rb:284:in `find': Couldn't find Word with 'id'=5 (ActiveRecord::RecordNotFound)
```
例外`ActiveRecord::RecordNotFound`が発生
#### find_by
```
irb(main):070:0> Word.find_by(id: 5)
  Word Load (0.7ms)  SELECT `words`.* FROM `words` WHERE `words`.`id` = 5 LIMIT 1
=> nil
```
`nil`を返す
#### where
```
irb(main):073:0> Word.where("id > 5")
  Word Load (0.9ms)  SELECT `words`.* FROM `words` WHERE (id > 5)
=> []
```
空きの配列に見えるが
```
irb(main):074:0> Word.where("id > 5").class
=> Word::ActiveRecord_Relation
```
型は`ActiveRecord_Relation`でした。