すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Rails

【Rails】 whereメソッドを使って欲しいデータの取得をしよう!

ぴっかちゃん
ぴっかちゃん

whereメソッドとは、テーブル内の条件に一致したレコードを配列の形で取得することができるメソッドです。

コントローラー | 使い方
1
モデル名.where("条件")

具体的には下記のように記述します。

コンソール | 具体例
1
2
3
4
5
6
7
8
9
# ageカラムに20が入っているレコードを全て取得
@users = User.where(age: 20)
  User Load (0.4ms)  SELECT `users`.* FROM `users` WHERE `users`.`age` = 20
=> [#<User:0x007fd316ec6710
  id: 1,
  name: "ぴかこ",
  age: 20,
  height: 170,
  weight: 60,>]

特定のレコードだけ取得したり、検索機能を実装したりするときに使うメソッドです。

whereメソッドの基本的な使い方

それではどういう時にどのようにwhereメソッドを使うかを確認していきましょう。

whereメソッドの記述方法

whereメソッドはシンボル指定と文字列指定の2つの方法があります。
順番にそれぞれの指定方法を確認していきましょう。

シンボル指定の方法

シンボル指定の場合は下記のように記述します。

コントローラー | シンボル指定の方法
1
2
3
4
5
6
7
8
9
10
11
12
13
モデル名.where(カラム名: 条件)

# nameカラムから「ぴっか」という文字が入っているレコードを全て取得
User.where(name: "ぴっか")

# 発行されるSQL
User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'ぴっか'
=> [#<User:0x007fd316cdeee8
  id: 3,
  name: "ぴっか",
  age: 30,
  height: 155,
  weight: 46>]

文字列指定の方法

文字列指定の場合は下記のように記述します。

コントローラー | 文字列指定の方法
1
2
3
4
5
6
7
8
9
10
11
12
13
モデル名.where("カラム名 = 条件")

# nameカラムから「ぴっか」という文字が入っているレコードを全て取得
User.where("name = 'ぴっか'")

# 発行されるSQL
User Load (17.2ms)  SELECT `users`.* FROM `users` WHERE (name = 'ぴっか')
=> [#<User:0x007fd313623f70
  id: 3,
  name: "ぴっか",
  age: 30,
  height: 155,
  weight: 46>]

このように書き方としては2つの方法がありますが、実際は1つの条件で検索をするときにはwhere(カラム名: "条件")のようにシンボル指定で定義します。
これはSQLインジェクションというセキュリティ上の脆弱性を防ぐためです。

SQLインジェクションとは

SQLを呼び出す際にセキュリティ上の不備を利用して、データベースのデータを不正に操作する攻撃方法のことを言います。

例えば下記のようなコードがあったとします。

コントローラー | 指定したidのユーザーのレコードを取得
1
2
id = params[:id]
User.where("id = #{id}")

もしユーザーidが1のユーザーのときは下記のようなSQLが発行されデータを取得します。

ターミナル | 発行されるSQL
1
SELECT `users`.* FROM `users` WHERE (id = 1)

そしてユーザーidが1のユーザーのレコードを取得できます。

もしこの1の部分に1 OR 1 = 1という値が入ってしまうとどうなるでしょうか?

コントローラー | id = '1 OR 1 = 1'が代入
1
2
id = '1 OR 1 = 1'
User.where("id = #{id}")

すると下記のようなSQLが発行されてしまいます。

ターミナル | 発行されるSQL
1
SELECT `users`.* FROM `users` WHERE (id = 1 OR 1 = 1)

上のコードが何を意味しているかというと、この後解説をするORによってid = 11 = 1の2つの式を判定します。
SQLではWHEREにtrueが渡ると全てのレコードを取得します。
この時1 = 1の式がtrueと判断されるため、全てのレコードが取得できてしまいます。
これだと全てのユーザーのデータが取得されてしまうため、セキュリティ上困りますよね。

ですがハッシュ形式で記述しておけばこのようなことは起こりませんので不正な値を取得されないというわけです。

ターミナル | 発行されるSQL
1
2
3
4
User.where(id: id)

# 発行されるSQL
SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
ぴっかちゃん

Rails(O/Rマッパー)を使用していると、SQLの知識は要らないと思われがちですが、「無駄なクエリの発行、データの引き出しの間違え」は、SQLで確認できるのです!

他にもパフォーマンス・デバック・複雑な操作の際にもSQLの知識は必要になります。

SQLの知識がなく、「データベースの操作だけできれば良い」と考えているとプログラムの品質や信頼を失ってしまいます。

SQLについて不安がある方は、基礎から一通り学べる以下の参考書を利用すると良いでしょう。

スッキリわかるSQL入門 第2版

発売から数年で不動の定番テキストとなった大人気SQL入門書に、最新のDBに対応した改訂版が登場!

プレースホルダの記述の仕方

SQLインジェクションを防ぐ書き方はもう一つあります。
それがプレースホルダーを使った書き方です。

コントローラー | プレースホルダーを使った書き方
1
2
3
4
モデル名.where("name = ?", "pikawaka")
# 上のコードは下記のコードと同じ
モデル名.where("name = 'pikawaka'")
モデル名.where(name: "pikawaka")

?はプレースホルダーと呼ばれ、第二引数で指定した値が置き換えられます。
上の例のpikawakaの部分には変数が入ることが多いです。
?を使うときにはwhere(カラム名: "条件")のような書き方はできません。

プレースホルダーを使った書き方は、この後説明する比較して条件を定義するときやandorを使うときにも使います。

whereメソッドの様々な検索方法・複雑な指定方法

whereメソッドには様々な検索方法があります。
順番に確認していきましょう。

比較して条件を定義

whereメソッドの条件には下記のように比較をして定義することができます。
このときもプレースホルダーを使いましょう。

コントローラー | 比較してレコードを取得
1
2
3
4
5
User.where("id > ?", 5)

# 発行されるSQL
User Load (36.8ms)  SELECT `users`.* FROM `users` WHERE (id > 5)
# idが5以上のレコードが全て取得できる

複数カラム指定でのAND検索方法

下記のように記述するとどちらの条件にも当てはまるレコードを取得することができます。

シンボルの場合

コントローラー | シンボルで複数の指定
1
2
3
4
5
6
7
8
9
10
User.where(name: "ぴっか", age: 25)

# 発行されるSQL
User Load (2.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'ぴっか' AND `users`.`age` = 25
=> [#<User:0x007fd316e2ecf8
  id: 3,
  name: "ぴっか",
  age: 25,
  height: nil,
  weight: 22>]

このようにnameカラムに「ぴっか」、ageカラムに「25」が存在するレコードを取得することができます。
どちらか一方でも当てはまらないレコードは取得しません。

文字列の場合

文字列でも指定することが出来ます。
その際はプレースホルダーの?を使用します。
下のコードはnameカラムにぴっかがあり、さらにageカラムに25があるレコードを全て取得します。

コントローラー | 文字列で複数の指定
1
2
3
4
5
6
7
8
9
10
User.where("name = ? and age = ?", "ぴっか", 25)

# 発行されるSQL
User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE (name = 'ぴっか' and age = 25)
=> [#<User:0x007fd316e2ecf8
  id: 3,
  name: "ぴっか",
  age: 25,
  height: 167,
  weight: 52>]

複数カラムを指定するときのSQLを見るとAND句が使われていることがわかります。

複数カラム指定でのOR検索方法

条件にorを使うとどちらかの条件に当てはまるレコードを取得することができます。

シンボルの場合

シンボルで指定する場合は下記のように記述します。

コントローラー | 条件にorを使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
User.where(name: "ぴっか").or(User.where(age: 22))

# 発行されるSQL
User Load (0.4ms)  SELECT `users`.* FROM `users` WHERE (`users`.`name` = 'ぴっか' OR `users`.`age` = 22)
=> [#<User:0x007fd318858b60
  id: 1,
  name: "ぴかこ",
  age: 22,
  height: 170,
  weight: 60,>,
 #<User:0x007fd318858a20
  id: 3,
  name: "ぴっか",
  age: 25,
  height: 167,
  weight: 52>]

このように上のコードはnameカラムにぴっかがあるかageカラムに22があるレコードを全て取得します。
つまりどちらか1つでも当てはまるレコードを全て取得してくれます。

文字列の場合

文字列の場合はプレースホルダーを使い下記のように記述します。

コントローラー | 条件にorを使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
User.where("name = ? or age = ?", "ぴっか", 22)

# 発行されるSQL
User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE (name = 'ぴっか' or age = 22)
=> [#<User:0x007fd318858b60
  id: 1,
  name: "ぴかこ",
  age: 22,
  height: 170,
  weight: 60,>,
 #<User:0x007fd318858a20
  id: 3,
  name: "ぴっか",
  age: 25,
  height: 167,
  weight: 52>]

not検索

not検索は条件を満したレコード以外を取得する時に使います。

シンボルの場合

シンボルの場合は下記のように定義します。

コントローラー | シンボルのnot検索 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
モデル名.where.not("条件")
# 例
User.where.not(name: "ぴっか")

# 発行されるSQL
User Load (0.4ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` != 'ぴっか'
=> [#<User:0x007fd316ca46f8
  id: 1,
  name: "ぴかこ",
  age: 25,
  height: 170,
  weight: 60>,
 #<User:0x007fd316ca4590
  id: 2,
  name: "ぴかわか",
  age: 67,
  height: 156,
  weight: 60>,
# ...略

上のように記述するとnameカラムの「ぴっか」という文字が入っているレコード以外のレコードを全て取得します。
複数指定する場合は下記のように記述します。

コントローラー | notメソッド -->
1
2
3
User.where.not(name: "ぴっか", height: 160)
# 下のコードと同じ意味
User.where.not(name: "ぴっか").where.not(hegiht: 160)

上のように記述するとnameカラムに「ぴっか」という文字が入っているレコードと、heightカラムが160以外のレコードを全て取得します。
どちらか一方が入っていればそのレコードは取得されません。

文字列の場合

文字列で指定する場合は下記のように記述します。

コントローラー | 文字列のnot検索 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
モデル名.where("NOT(条件)")
# 例
User.where("NOT(name= 'ぴっか')")

# 発行されるSQL
User Load (2.5ms)  SELECT `users`.* FROM `users` WHERE (NOT(name= 'ぴっか'))
=> [#<User:0x007fd316ca46f8
  id: 1,
  name: "ぴかこ",
  age: 25,
  height: 170,
  weight: 60>,
 #<User:0x007fd316ca4590
  id: 2,
  name: "ぴかわか",
  age: 67,
  height: 156,
  weight: 60>,
# ...略

NOT NULL

SQLではカラムの値がNULL(rubyではnil)以外を取得するにはIS NOT NULLと記述します。
whereメソッドの条件にこれを記述するとカラムの値がNULL以外のレコードを全て取得することができます。

シンボルの場合

シンボルの場合は下記のように定義します。

コントローラー | NOT NULLの別の書き方 -->
1
2
3
4
User.where.not(name: nil)

# 発行されるSQL
SELECT `users`.* FROM `users` WHERE `users`.`name` IS NOT NULL

文字列の場合

文字列の場合は下記のように定義します。

コントローラー | NOT NULL -->
1
2
3
4
5
# nameカラムがNULL以外のレコードを全て取得
User.where("name IS NOT NULL")

# 発行されるSQL
SELECT `users`.* FROM `users` WHERE (name IS NOT NULL)

このように記述するとnameカラムの値が存在するレコードだけを取得することができます。
ただしこの記述よりシンボルを使って書くのが一般的です。

複数の条件(in句)

whereメソッドの条件に配列を使うと複数の条件を定義することができます。

シンボルの場合

シンボルの場合は下記のように定義します。

コントローラー | 複数の条件を指定 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
User.where(id: [1,2])

# 発行されるSQL
User Load (42.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 2)
=> [#<User:0x007fd3180a9f18
  id: 1,
  name: "ぴかこ",
  age: 25,
  height: 170,
  weight: 60>,
 #<User:0x007fd3180a9dd8
  id: 2,
  name: "ぴかわか",
  age: 67,
  height: 156,
  weight: 60>]

上のように記述するとidカラムが1と2のレコードが取得できます。
このように配列で指定するとSQLのIN句が使われているのが確認できます。

文字列の場合

文字列の場合は下記のように定義します。

コントローラー | 複数の条件を指定 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
User.where("id IN (1, 2)")

# 発行されるSQL
User Load (4.3ms)  SELECT `users`.* FROM `users` WHERE (id IN (1, 2))
=> [#<User:0x007fd3180a9f18
  id: 1,
  name: "ぴかこ",
  age: 25,
  height: 170,
  weight: 60>,
 #<User:0x007fd3180a9dd8
  id: 2,
  name: "ぴかわか",
  age: 67,
  height: 156,
  weight: 60>]

あいまい検索 LIKE

LIKE句は、あいまいな文字列の検索をするときに使います。

like検索

あいまい検索は下記のように記述します。

コントローラー | like句の使い方 -->
1
2
3
4
5
6
モデルクラス.where("カラム名 LIKE?", "検索したい文字列") 
# 例 nameカラムに「あ」という文字が入っているレコードを全て取得
User.where("name LIKE?", "%あ%")

# 発行されるSQL
User Load (20.6ms)  SELECT `users`.* FROM `users` WHERE (name LIKE'%あ%')

上の検索したい文字列には%_を文字列と組み合わせた値として記述します。
それぞれ下記の意味を持ちます。

記号 意味
% 空白文字を含む任意の複数文字列
_ 任意の1文字

これだけだとわかりづらいので例を見てみましょう。

%は任意の複数文字なのであ%と書くと以降はどんな文字も複数含むので先頭にがついた文字列は全て含まれることになります。
(例)あ% = りがとうございます

%あと書くと末尾がで終わる全ての文字列が含まれます。
(例)%は = こんにち

%が複数文字に対し、_は任意の1文字を示します。
あ_とするとから始まる2文字の単語になります。
(例)あ_ = め、お など

_あとするとで終わる2文字の単語になります。
(例)_く = か、ま など

like句は検索機能を実装したい時などに使います。

コントローラー | like句の使い方 -->
1
users = User.where("name LIKE?", "%あ%")

上の例の場合、検索したい文字列を%あ%と指定しました。
ここでは前後に%をつけたので文字列の中にが含まれればどんな文字列も当てはまることになります。
ですのでusersテーブルのnameカラムの中のが含まれるレコード全てが取得できるというわけです。

not like検索

上の例は当てはまる場合のレコードを取得しましたが、逆にあてはまらない場合のレコードを取得する際にはnot like検索を使用します。

コントローラー | not like句の使い方 -->
1
2
3
4
5
# 例 nameカラムに「あ」という文字が入っていないレコードを全て取得
User.where("name not LIKE?", "%あ%")

# 発行されるSQL
User Load (2.4ms)  SELECT `users`.* FROM `users` WHERE (name not LIKE'%あ%')

上のように記述するとnameカラムに「あ」という文字が入っていない全てのレコードを取得することができます。

joinsメソッドで関連するモデル間の検索

アソシエーションを組んでいると関連するモデル間の検索を行うことが出来ます。
その際に使用するメソッドがjoinsメソッドです。

コントローラー | 関連するモデル間の検索 -->
1
2
3
モデル名.joins(:関連名).where(カラム名: )
# 例
Owner.joins(:cats).where(name: "田中")

詳しい定義方法や説明はjoinsメソッドの記事のwhereメソッドで条件を追加してみようを参照してください。

where(条件).firstをfind_byメソッドに書き換え

whereメソッドは条件に一致した全てのレコードを取得するメソッドでした。
条件には一致するけど全てを取得したくない場合はどうすればよいでしょうか?

where(条件).firstの場合

とりあえず条件に一致するレコードを1つ取得したい場合はfirstメソッドを使用します。
firstメソッドは最初のレコードを1つ取得するメソッドです。

コントローラー | firstメソッドの使い方 -->
1
2
3
4
5
6
モデル名.where(条件).first
# nameカラムに「あ」という文字が入っている最初のレコードを取得
User.where("name LIKE?", "%あ%").first

# 発行されるSQL
User Load (1.0ms)  SELECT  `users`.* FROM `users` WHERE (name LIKE'%あ%') ORDER BY `users`.`id` ASC LIMIT 1

find_byの場合

find_byメソッドは条件に一致する最初のレコード1件を取得するメソッドです。

コントローラー | find_byメソッドの使い方 -->
1
2
3
4
5
6
モデル名.where(条件).first
# nameカラムに「あ」という文字が入っている最初のレコードを取得
User.find_by("name like '%あ%'")

# 発行されるSQL
User Load (0.9ms)  SELECT  `users`.* FROM `users` WHERE (name like '%あ%') LIMIT 1

この2つをどう使い分けるかは世界で一番詳しいfind_byメソッドのいろいろな使い方の記事を参照してください。

検索機能を実装してみよう

whereメソッドを使うと検索機能を実装することができます。
実際のコードの例を紹介します。

検索するためのアクションの定義

この例では検索を行うためにsearchアクションを定義しました。
検索フォームは下記のように記述したとします。

ビューファイル | 検索フォームの記述
1
2
3
4
<%= form_with(url: searchアクションが動くパス, local: true) do |form| %>
  <%= form.text_field :keyword  %>
  <%= form.submit "検索" %>
<% end %>

コントローラーのsearchアクションは下記のように記述します。

コントローラー | seachアクションの定義 -->
1
2
3
4
5
6
class UsersController < ApplicationController
    def search
        keyword = params[:keyword]
        @users = User.where('name LIKE?', "%#{keyword}%")
    end
end

今回は検索フォームにform.text_field :keywordと記述したので、このフォームに入力した値はコントローラーでparams[:keyword]と書くことで取得することができます。
コントローラーでは取得した値をkeywordという変数に代入しました。
それがwhereメソッドのLIKE句の値に入り、そのワードが含まれたレコードを取得してくれるというわけです。

paramsについてはparamsについて徹底解説!の記事も参考にしてみてください。

この記事のまとめ

  • whereメソッドは、テーブル内の条件に一致したレコードを配列の形で取得することができるメソッド
  • findメソッドとは異なり、複数のレコードを取得することが出来る
  • 検索機能を実装したりするときに使用する