窓際BLOG

プログラミングの学習メモや書籍の感想等を公開していきます。

RubyonRails:Rails5で使えるデータ検索関連のメソッドをまとめてみた

f:id:madogiwa0124:20170729104257p:plain

現在、Railsブロンズ試験を受験してみようと思い、RubyonRails5アプリケーションプログラミングという本で勉強中なので、頭の整理も兼ねて学んだことをまとめてみるφ(..)

Ruby on Rails 5アプリケーションプログラミング

Ruby on Rails 5アプリケーションプログラミング

今回はモデルの検索について主要なメソッドをまとめてみました・ω・

モデルの検索処理まとめ

主キーによる検索:findメソッド

findメソッドは、主キーによるレコードの検索を行います。
取得結果は主キー指定の場合はモデルのインスタンス、複数の主キーを指定した場合は、モデルのインスタンスの配列が返却されます。

# 主キーによる検索
Model.find(1)
#=> SELECT  "models".* FROM "models" WHERE "models"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]

# 複数の主キーによる検索
Model.find([1, 2, 3])
#=> LECT "models".* FROM "models" WHERE "models"."id" IN (1, 2, 3)

任意キーによる検索:find_byメソッド

find_byメソッドは、任意の項目をキーとして検索を行います。
取得結果として検索条件に合致した最初の一件を返却します。
また複数の検索キーが引数に設定された場合は、複数の検索キー(AND条件)で結果を取得します。

# タイトルをキーとして検索
Model.find_by(title: "hoge")
#=> SELECT  "models".* FROM "models" WHERE "models"."title" = ? LIMIT ?  [["title", "hoge"], ["LIMIT", 1]]

# タイトルと内容をキーとして検索
Model.find_by(title: "hoge").find_by(content: "fuga")
#=> SELECT  "models".* FROM "models" WHERE "models"."title" = ? AND "models"."content" = ? LIMIT ?  [["title", "hoge"], ["content", "fuga"], ["LIMIT", 1]]

基本的な条件式を設定する:whereメソッド

whereメソッドは、引数に条件を設定し、与えられた条件に合致するレコードを返却します。
find_byメソッドと一見似ていますが、whereメソッドは、条件に合致する全てのレコードを返却します。
また、プレースホルダ?を使い任意の値を条件式に埋め込むことが出来ます。
※取得結果が1件だったとしてもActiveRecord::Relationで返却されるため注意が必要です。

madogiwa0124.hatenablog.com

# タイトルがhogeかつ本文がfugaのレコードを検索
# hashで複数条件を与えた場合はfind_byメソッドと同様にAND条件となる。
Model.where(title: "hoge", content: "fuga")
#=> SELECT  "models".* FROM "models" WHERE "models"."title" = ? AND "models"."content" = ? LIMIT ?  [["title", "hoge"], ["content", "fuga"], ["LIMIT", 11]]

# 金額が1000以上のレコードを検索
Model.where('price >= ?', 1000)
#=> SLECT  "models".* FROM "models" WHERE (price >= 1000) LIMIT ?  [["LIMIT", 11]]

条件に合致しないレコードを検索する:notメソッド

notメソッドを使うことで、whereメソッドで指定した条件に合致しないレコードを取得することが出来ます。

# タイトルがhogeじゃないレコードを検索
Model.where.not(title: "hoge")
#=> SELECT  "models".* FROM "models" WHERE "models"."title" != ? LIMIT ?  [["title", "hoge"], ["LIMIT", 11]]

OR条件で複数条件を設定する:orメソッド

orメソッドは与えられた条件式をOR条件(または)として加えることが出来ます。
ただし、加えられるのは同一モデルに対するwhereメソッドのみです。

# タイトルがhogeまたは本文がfugaのレコードを検索
Model.where(title: "hoge").or(Model.where(content: "fuga"))
#=> SELECT  "models".* FROM "models" WHERE ("models"."title" = ? OR "models"."content" = ?) LIMIT ?  [["title", "hoge"], ["content", "fuga"], ["LIMIT", 11]]

取得結果を並び変える:orderメソッド

orderメソッドに項目とソート順を与えることで取得結果の並び替えることが出来ます。
項目とソート順は複数与え、複数項目での並び替えを行うことも可能です。

# タイトルの昇順で並び変える
Model.order(title: :asc)
#=> SELECT  "models".* FROM "models" ORDER BY "models"."title" ASC LIMIT ?  [["LIMIT", 11]]

# タイトルの降順で並び変える
Model.order(title: :desc)
#=> SELECT  "models".* FROM "models" ORDER BY "models"."title" DESC LIMIT ?  [["LIMIT", 11]]

# 価格の降順、タイトルの昇順で並び変える
Model.order(price: :desc, title: :asc)
# => LECT  "models".* FROM "models" ORDER BY "models"."price" DESC, "models"."title" ASC LIMIT ?  [["LIMIT", 11]]

取得する項目を指定する:selectメソッド

selectメソッドを用いることで特定の項目のみを取得することが出来ます。
引数を複数与えることで、複数の項目を指定することも可能です。

# 全てのモデルのタイトルを取得する
Model.select(:title)
#=> SELECT  "models"."title" FROM "models" LIMIT ?  [["LIMIT", 11]]

# 全てのモデルのタイトルと価格を取得する
Model.select(:title, :price)
#=> SELECT  "models"."title", "models"."price" FROM "models" LIMIT ?  [["LIMIT", 11]]

重複したレコードを削除する:distinctメソッド

distinctメソッドを使うことで取得結果から重複したレコードを削除することが出来ます。

# モデルに設定されているジャンルの一覧を重複なしで取得
Model.select(:genre).distinct
#=> SELECT  DISTINCT "models"."genre" FROM "models" LIMIT ?  [["LIMIT", 11]]

レコードの取得件数を制御する:limitメソッド・offsetメソッド

limitメソッドを使用することで最大取得件数を制限することが出来ます。
また、offsetメソッドを使用することで、取得開始位置を指定することが出来ます。

# モデルを最大3件取得する
Model.limit(3)
# => SELECT  "models".* FROM "models" LIMIT ?  [["LIMIT", 3]]

# モデルを10件目から10件取得する
# offsetを指定する際は先頭行が0となるため注意
Model.offset(10).limit(10)
# => SELECT  "models".* FROM "models" LIMIT ? OFFSET ?  [["LIMIT", 10], ["OFFSET", 10]]

データの集計を行う:groupメソッド

groupメソッドを使用することで特定のキーで結果をグルーピングし、データの集計を行うことが出来ます。

# ジャンル別の最大価格を取得する
Model.select('genre, MAX(price) AS max_price').group(:genre)
# => SELECT genre, MAX(price) as max_price FROM "models" GROUP BY "models"."genre"

集計後の結果に検索条件を設定する:havingメソッド

havingメソッドは、集計後の結果に対して条件を設定し絞込を行うことが出来ます。

# ジャンル別の最大価格が10000以上のものだけを取得する
Model.select('genre, MAX(price) AS max_price').group(:genre).having('MAX(price) >= ?', 10000)
# => SELECT  genre, MAX(price) as max_price FROM "models" GROUP BY "models"."genre" HAVING (MAX(price) >= 10000) LIMIT ?  [["LIMIT", 11]]

特定項目の平均値・最小値・最大値・合計値を取得する:averageメソッド・minimumメソッド・maximumメソッド・sumメソッド

平均・最小・最大・合計といった集計でよく使いそうなものは、メソッドとして定義されています。
これらのメソッドはwhereメソッドに続けて記述することによって、検索結果に適用することも出来ます。

# 価格の平均値を取得
Model.average(:price)
# => SELECT AVG("models"."price") FROM "models"

# 価格の最小値を取得
Model.minimum(:price)
# => SELECT MIN("models"."price") FROM "models"

# 価格の最大値を取得
Model.maximum(:price)
# => SELECT MAX("models"."price") FROM "models"

# 価格の合計値を取得
Model.sum(:price)
# => SELECT SUM("models"."price") FROM "models"

# 特定ジャンルの平均価格を取得
Model.where(genre: "hoge").average(:price)
# => SELECT AVG("models"."price") FROM "models" WHERE "models"."genre" = ?  [["genre", "hoge"]]

生のSQLを使って検索を行う:find_by_sqlメソッド

どうしても生のSQLで検索を行いたい場合は、find_by_sqlメソッドを使用します。
SQLの構文はDBMSに依存するためDBMS変更時に不具合を誘発する可能性があるため注意してください。

# 特定ジャンルのレコードを取得
Model.find_by_sql(['SELECT * FROM models WHERE genre = ?', 'hoge'])
# => SELECT * FROM models WHERE genre = 'hoge'

以上です!
また何かあれば追記しようと思いますφ(..)