Railsではデータベースを直接操作せずとも、モデルを使ってデータベースの情報を取得・更新・削除するといった操作ができます。
テーブルの情報を検索する時に、最も柔軟な検索条件の絞り込み方法にwhereメソッドがあります。
ここでは、whereメソッドの使い方について解説しています。
- whereメソッドの基本的な使い方
- whereメソッドの使い方一覧
- カラム名と値を指定 (カラム名: 値)
- 複数の条件を指定 (カラム名: [値1, 値2,,,])
- 以上・以下の指定 (“カラム名 > ?”, 数値)、(“カラム名 <= ?”, 数値)
- かつ(AND)の指定 where(条件).where(条件)
- かつ(AND)の指定 (“カラム名1=? and カラム名2=?”, 値1, 値2)
- または (条件).or(モデル名.where(条件))
- または(or)の指定 (“カラム名1=? or カラム名2=?”, 値1, 値2)
- 否定 where.not(条件)
- nil以外 where.not(カラム名: nil)
- パターンを指定 (“カラム名 like?”, “パターン”)
- 否定パターンを指定 (“カラム名 not like?”, “パターン”)
whereメソッドの基本的な使い方
whereメソッドは引数で条件を指定します。
モデル名.where(条件)
ActiveRecord::Relationとは?
Railsのテーブル操作で結果として返ってくる型には、主に次の3つがあります。
- 配列
- オブジェクト
- ActiveRecord::Relation
ActiveRecord::Relation
とは、モデルを使ってSQLクエリを実行できる型です。
このため、whereメソッドで取得した結果に、.findやfirstなどのテーブルを検索するメソッドをつなげることができます。
実例
#ActiveRecord::Relationインスタンスの例
irb(main):026:0> Test.where(id:11)
Test Load (0.5ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" = $1 /* loading for inspect */ LIMIT $2 [["id", 11], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Test id: 11, name: "ユーザー4", created_at: "2021-07-20 02:14:00.922908000 +0000", updated_at: "2021-07-20 02:14:00.922908000 +0000">]>
#メソッドをつなげる例
irb(main):028:0> Test.where(id:[10,11,12,13]).third
Test Load (0.7ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" IN ($1, $2, $3, $4) ORDER BY "tests"."id" ASC LIMIT $5 OFFSET $6 [["id", 10], ["id", 11], ["id", 12], ["id", 13], ["LIMIT", 1], ["OFFSET", 2]]
=> #<Test id: 12, name: "ユーザー5", created_at: "2021-07-20 02:14:06.506699000 +0000", updated_at: "2021-07-20 02:14:06.506699000 +0000">
irb(main):029:0>
空のデータと存在しないテーブルを指定した場合
#空のデータ
irb(main):043:0> Test.where(name:"ユーザー")
Test Load (0.6ms) SELECT "tests".* FROM "tests" WHERE "tests"."name" = $1 /* loading for inspect */ LIMIT $2 [["name", "ユーザー"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
#存在しないテーブル
irb(main):044:0> Test.where(dummy:"ユーザー")
Traceback (most recent call last):
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column tests.dummy does not exist)
LINE 1: SELECT "tests".* FROM "tests" WHERE "tests"."dummy" = $1 /* ...
whereメソッドの使い方一覧
whereメソッドでは次のような検索方法が使えます。
- カラム名と値を指定
(カラム名: 値)
- 複数の条件を指定
(カラム名: [値1, 値2,,,])
- 以上、以下の指定
("カラム名 > ?", 数値)
、("カラム名 <= ?", 数値)
- かつ(AND)の指定
where(条件).where(条件)
- かつ(AND)の指定
("カラム名1=? and カラム名2=?", 値1, 値2)
- または(or)の指定
(条件).or(モデル名.where(条件))
- または(or)の指定
("カラム名1=? or カラム名2=?", 値1, 値2)
- 否定
where.not(条件)
- nil以外
where.not(カラム名: nil)
- パターンを指定
("カラム名 like?", "パターン")
- 否定パターンを指定
("カラム名 not like?", "パターン")
カラム名と値を指定 (カラム名: 値)
カラム名と値がわかっている時はwhereメソッドの引数に(カラム名: 値)
を渡します。
モデル名.where(カラム名: 値)
実例
#idカラムの値が11のデータを検索
irb(main):026:0> Test.where(id:11)
Test Load (0.5ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" = $1 /* loading for inspect */ LIMIT $2 [["id", 11], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Test id: 11, name: "ユーザー4", created_at: "2021-07-20 02:14:00.922908000 +0000", updated_at: "2021-07-20 02:14:00.922908000 +0000">]>
#nameカラムの値が「ユーザー10」のデータを検索
irb(main):042:0> Test.where(name:"ユーザー10")
Test Load (0.8ms) SELECT "tests".* FROM "tests" WHERE "tests"."name" = $1 /* loading for inspect */ LIMIT $2 [["name", "ユーザー10"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Test id: 17, name: "ユーザー10", created_at: "2021-07-20 02:14:50.693377000 +0000", updated_at: "2021-07-20 02:14:50.693377000 +0000">]>
データがない場合
#対象のデータがない場合
irb(main):043:0> Test.where(name:"ユーザー")
Test Load (0.6ms) SELECT "tests".* FROM "tests" WHERE "tests"."name" = $1 /* loading for inspect */ LIMIT $2 [["name", "ユーザー"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
#テーブルが存在しない場合
irb(main):044:0> Test.where(dummy:"ユーザー")
Traceback (most recent call last):
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column tests.dummy does not exist)
LINE 1: SELECT "tests".* FROM "tests" WHERE "tests"."dummy" = $1 /* ...
findとfind_byとの比較
where(カラム名: 値)はfindやfind_byととても似た処理になります。
#find
irb(main):048:0> Test.find(11)
Test Load (0.6ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" = $1 LIMIT $2 [["id", 11], ["LIMIT", 1]]
=> #<Test id: 11, name: "ユーザー4", created_at: "2021-07-20 02:14:00.922908000 +0000", updated_at: "2021-07-20 02:14:00.922908000 +0000">
#オブジェクトなので値を取得できる
irb(main):049:0> Test.find(11).name
Test Load (0.8ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" = $1 LIMIT $2 [["id", 11], ["LIMIT", 1]]
=> "ユーザー4"
#find_by
irb(main):051:0> Test.find_by(name:"ユーザー10")
Test Load (0.6ms) SELECT "tests".* FROM "tests" WHERE "tests"."name" = $1 LIMIT $2 [["name", "ユーザー10"], ["LIMIT", 1]]
=> #<Test id: 17, name: "ユーザー10", created_at: "2021-07-20 02:14:50.693377000 +0000", updated_at: "2021-07-20 02:14:50.693377000 +0000">
#オブジェクトなので値を取得できる
irb(main):052:0> Test.find_by(name:"ユーザー10").id
Test Load (0.5ms) SELECT "tests".* FROM "tests" WHERE "tests"."name" = $1 LIMIT $2 [["name", "ユーザー10"], ["LIMIT", 1]]
=> 17
複数の条件を指定 (カラム名: [値1, 値2,,,])
値を[値1, 値2,,,]
のように配列型式にすれば複数の条件を指定することもできます。
モデル名.where(カラム名: [値1, 値2,,,])
実例
#idカラムの値が8, 9, 14のデータを検索
irb(main):046:0> Test.where(id:[8,9,14])
Test Load (0.6ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" IN ($1, $2, $3) /* loading for inspect */ LIMIT $4 [["id", 8], ["id", 9], ["id", 14], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Test id: 8, name: "ユーザー1", created_at: "2021-07-20 00:30:22.334075000 +0000", updated_at: "2021-07-20 00:30:22.334075000 +0000">, #<Test id: 9, name: "ユーザー2", created_at: "2021-07-20 00:30:31.939292000 +0000", updated_at: "2021-07-20 00:30:31.939292000 +0000">, #<Test id: 14, name: "ユーザー7", created_at: "2021-07-20 02:14:32.483212000 +0000", updated_at: "2021-07-20 02:14:32.483212000 +0000">]>
#nameカラムの値が「ユーザー10」と「ユーザー1」のデータを検索
irb(main):053:0> Test.where(name: ["ユーザー10", "ユーザー1"])
Test Load (1.0ms) SELECT "tests".* FROM "tests" WHERE "tests"."name" IN ($1, $2) /* loading for inspect */ LIMIT $3 [["name", "ユーザー10"], ["name", "ユーザー1"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Test id: 8, name: "ユーザー1", created_at: "2021-07-20 00:30:22.334075000 +0000", updated_at: "2021-07-20 00:30:22.334075000 +0000">, #<Test id: 17, name: "ユーザー10", created_at: "2021-07-20 02:14:50.693377000 +0000", updated_at: "2021-07-20 02:14:50.693377000 +0000">]>
値が存在しない場合
#一部の値がない場合(id=1と2が存在しない)
irb(main):055:0> Test.where(id:[8, 10, 1, 2 ])
Test Load (0.9ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" IN ($1, $2, $3, $4) /* loading for inspect */ LIMIT $5 [["id", 8], ["id", 10], ["id", 1], ["id", 2], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Test id: 8, name: "ユーザー1", created_at: "2021-07-20 00:30:22.334075000 +0000", updated_at: "2021-07-20 00:30:22.334075000 +0000">, #<Test id: 10, name: "ユーザー3", created_at: "2021-07-20 00:30:42.764326000 +0000", updated_at: "2021-07-20 00:30:42.764326000 +0000">]>
#値が一つもない場合
irb(main):054:0> Test.where(id:[1,2])
Test Load (0.8ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" IN ($1, $2) /* loading for inspect */ LIMIT $3 [["id", 1], ["id", 2], ["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
以上・以下の指定 (“カラム名 > ?”, 数値)、(“カラム名 <= ?”, 数値)
引数で、(“カラム名 不等号 ?”, 数値) を指定すれば、条件に一致するデータのみを取得することができます。
モデル名.where("カラム名 > ?", 数値)
モデル名.where("カラム名 <= ?", 数値)
実例
#カラムageが20より大きいを検索
irb(main):064:0> Student.where("age > ? ", 20)
Student Load (0.8ms) SELECT "students".* FROM "students" WHERE (age > 20 ) /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 1, name: "イチロー", age: 24, gender: "男性", created_at: "2021-07-20 01:18:23.633399000 +0000", updated_at: "2021-07-20 01:18:23.633399000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">, #<Student id: 5, name: "Danny", age: 32, gender: "男性", created_at: "2021-07-20 01:19:43.297445000 +0000", updated_at: "2021-07-20 01:19:43.297445000 +0000">]>
#カラムageが16以下を検索
irb(main):068:0> Student.where("age <= ? ", 16)
Student Load (0.9ms) SELECT "students".* FROM "students" WHERE (age <= 16 ) /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 3, name: "ケン", age: 16, gender: "男性", created_at: "2021-07-20 01:19:01.170922000 +0000", updated_at: "2021-07-20 01:19:01.170922000 +0000">]>
かつ(AND)の指定 where(条件).where(条件)
whereメソッドで取得した結果に対して、再度whereメソッドを使うと、両方に合致する結果のみを取得することができます。
モデル名.where(条件).where(条件)
実例
#where2個
irb(main):080:0> Student.where(gender: "女性").where("age > ?", 16)
Student Load (0.8ms) SELECT "students".* FROM "students" WHERE "students"."gender" = $1 AND (age > 16) /* loading for inspect */ LIMIT $2 [["gender", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 2, name: "ジェシー", age: 17, gender: "女性", created_at: "2021-07-20 01:18:44.653383000 +0000", updated_at: "2021-07-20 01:18:44.653383000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">]>
かつ(AND)の指定 (“カラム名1=? and カラム名2=?”, 値1, 値2)
whereメソッドをつなげる以外にも、whereメソッドの中でandを使って指定することもできます。
モデル名.where("カラム名1 = ? and カラム名2 = ?", 値1, 値2)
実例
#カラムageが20より大きく、30より小さい
irb(main):094:0> Student.where("age > ? and age < ? ", 20, 30)
Student Load (0.7ms) SELECT "students".* FROM "students" WHERE (age > 20 and age < 30 ) /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 1, name: "イチロー", age: 24, gender: "男性", created_at: "2021-07-20 01:18:23.633399000 +0000", updated_at: "2021-07-20 01:18:23.633399000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">]>
#カラムageが20より大きく、genderが女性のみ
irb(main):091:0> Student.where("age > ? and gender = ?", 20, 1)
Student Load (0.9ms) SELECT "students".* FROM "students" WHERE (age > 20 and gender = 1) /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">]>
または (条件).or(モデル名.where(条件))
orメソッドを使うと複数の条件をつなげることができます。
モデル名.where(条件).or(モデル名.where(条件))
実例
#女性と30歳以上の男性を検索
irb(main):076:0> Student.where(gender: "女性").or(Student.where("age >= ?", 30))
Student Load (0.8ms) SELECT "students".* FROM "students" WHERE ("students"."gender" = $1 OR age >= 30) /* loading for inspect */ LIMIT $2 [["gender", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 2, name: "ジェシー", age: 17, gender: "女性", created_at: "2021-07-20 01:18:44.653383000 +0000", updated_at: "2021-07-20 01:18:44.653383000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">, #<Student id: 5, name: "Danny", age: 32, gender: "男性", created_at: "2021-07-20 01:19:43.297445000 +0000", updated_at: "2021-07-20 01:19:43.297445000 +0000">]>
または(or)の指定 (“カラム名1=? or カラム名2=?”, 値1, 値2)
orメソッド以外にも、whereメソッドの中でorを使って指定することもできます。
モデル名.where("カラム名1 = ? or カラム名2 = ?", 値1, 値2)
実例
#25歳以上か女性を検索
irb(main):095:0> Student.where("age >= ? or gender = ? ", 25, 1)
Student Load (0.9ms) SELECT "students".* FROM "students" WHERE (age >= 25 or gender = 1 ) /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 2, name: "ジェシー", age: 17, gender: "女性", created_at: "2021-07-20 01:18:44.653383000 +0000", updated_at: "2021-07-20 01:18:44.653383000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">, #<Student id: 5, name: "Danny", age: 32, gender: "男性", created_at: "2021-07-20 01:19:43.297445000 +0000", updated_at: "2021-07-20 01:19:43.297445000 +0000">]>
否定 where.not(条件)
○○以外を指定したい場合は、whereの後ろでnotメソッドを繋げます。
モデル名.where.not(条件)
実例
#20歳未満を除く(20歳以上を検索)
irb(main):097:0> Student.where.not("age < ?", 20)
Student Load (0.7ms) SELECT "students".* FROM "students" WHERE NOT (age < 20) /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 1, name: "イチロー", age: 24, gender: "男性", created_at: "2021-07-20 01:18:23.633399000 +0000", updated_at: "2021-07-20 01:18:23.633399000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">, #<Student id: 5, name: "Danny", age: 32, gender: "男性", created_at: "2021-07-20 01:19:43.297445000 +0000", updated_at: "2021-07-20 01:19:43.297445000 +0000">]>
nil以外 where.not(カラム名: nil)
notメソッドを使うと、指定したカラムで値がnil以外のデータを抜き出すことができます。値にnil
を指定します。
モデル名.where.not(カラム名: nil)
実例
#ageカラムの値がnil以外
irb(main):100:0> Student.where.not(age: nil)
Student Load (0.5ms) SELECT "students".* FROM "students" WHERE "students"."age" IS NOT NULL /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 1, name: "イチロー", age: 24, gender: "男性", created_at: "2021-07-20 01:18:23.633399000 +0000", updated_at: "2021-07-20 01:18:23.633399000 +0000">, #<Student id: 2, name: "ジェシー", age: 17, gender: "女性", created_at: "2021-07-20 01:18:44.653383000 +0000", updated_at: "2021-07-20 01:18:44.653383000 +0000">, #<Student id: 3, name: "ケン", age: 16, gender: "男性", created_at: "2021-07-20 01:19:01.170922000 +0000", updated_at: "2021-07-20 01:19:01.170922000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">, #<Student id: 5, name: "Danny", age: 32, gender: "男性", created_at: "2021-07-20 01:19:43.297445000 +0000", updated_at: "2021-07-20 01:19:43.297445000 +0000">]>
パターンを指定 (“カラム名 like?”, “パターン”)
likeの後にパターンを指定することで、「○○を含む」「前方一致」「後方一致」といったあいまい検索をすることができます。
モデル名.where("カラム名 like ?", "パターン")
パターン指定の方法
パターン指定にはSQLのLIKE検索で使う記号を使います。(正規表現とは異なります)
記号 | 内容 |
---|---|
% | 任意の文字の繰り返し(空白文字を含む) |
_ | 任意の1文字 |
\ | エスケープ(後ろの記号を文字として扱う) |
パターン指定例
指定方法 | 記述例 | 内容 |
---|---|---|
後方一致 | %テスト | 末尾が「テスト」で終わる |
前方一致 | テスト% | 「テスト」で始まる |
含む | %テスト% | 「テスト」を含む |
後方1文字指定 | テスト_ | 「テスト」の後ろに1文字だけ続く |
後方2文字指定 | テスト__ | 「テスト」の後ろに2文字だけ続く |
前方1文字指定 | _テスト | 「テスト」の前に1文字ある |
前方2文字指定 | __テスト | 「テスト」の前に2文字ある |
文字列としての% | \% | 文字列の%として認識する |
文字列としての_ | \_ | 文字列の_として認識する |
%を含む | %\%% | %を含む文字 |
%で終わる | %\% | 末尾が%で終わる |
実例
#nameカラムに「ー」を含む
irb(main):104:0> Student.where("name like?", "%ー%")
Student Load (0.7ms) SELECT "students".* FROM "students" WHERE (name like'%ー%') /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 1, name: "イチロー", age: 24, gender: "男性", created_at: "2021-07-20 01:18:23.633399000 +0000", updated_at: "2021-07-20 01:18:23.633399000 +0000">, #<Student id: 2, name: "ジェシー", age: 17, gender: "女性", created_at: "2021-07-20 01:18:44.653383000 +0000", updated_at: "2021-07-20 01:18:44.653383000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">]>
否定パターンを指定 (“カラム名 not like?”, “パターン”)
likeの前にnotをつけると、指定した条件に一致しないものを検索することができます。
モデル名.where("カラム名 not like ?", "パターン")
実例
#nameカラムに「ー」を含まない
irb(main):111:0> Student.where("name not like?", "%ー%")
Student Load (0.7ms) SELECT "students".* FROM "students" WHERE (name not like'%ー%') /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 3, name: "ケン", age: 16, gender: "男性", created_at: "2021-07-20 01:19:01.170922000 +0000", updated_at: "2021-07-20 01:19:01.170922000 +0000">, #<Student id: 5, name: "Danny", age: 32, gender: "男性", created_at: "2021-07-20 01:19:43.297445000 +0000", updated_at: "2021-07-20 01:19:43.297445000 +0000">]>