LoginSignup
59
74

More than 5 years have passed since last update.

クエリメソッドをまとめてみた[Rails]

Posted at

はじめに

主なクエリメソッドをまとめました。

クエリメソッド

下記2つのメソッド(find,find_by )はクエリメソッドではありませんが、頭の整理のため載せておきます。

find

主キーを一つだけ指定

[1] pry(main)> Book.find(1)

これにより次のSQLが発行されます。

SELECT  `books`.* FROM `books` WHERE `books`.`id` = 35 LIMIT 1

主キーを複数指定

[2] pry(main)> Book.find([2,4,7])

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE `books`.`id` IN (2, 4, 7)

findメソッドは、引数に配列を渡すことで、それぞれの主キーに合致するレコード全てを取り出すことができます。

find_by

任意の列をキーに指定

[3] pry(main)> Book.find_by(title: "メタプログラミングRuby")

これにより次のSQLが発行されます。

SELECT  `books`.* FROM `books` WHERE `books`.`title` = 'メタプログラミングRuby' LIMIT 1

ヒットした最初の1件を取得します。

複数の任意の列キーを指定

[4] pry(main)> Book.find_by(title: "メタプログラミングRuby", price: 3240)

これにより次のSQLが発行されます。

SELECT  `books`.* FROM `books` WHERE `books`.`title` = 'メタプログラミングRuby' AND `books`.`price` = 3240 LIMIT 1

次からがクエリメソッドです。
クエリメソッドはfindfind_byと違って、その場ではデータベースにアクセスしません。
結果が必要になった時に初めて、データベースに問い合わせます(遅延ロード)

where

条件句を設定

[5] pry(main)> Book.where(price: 3218)

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE `books`.`price` = 3218

境界値を設定

[6] pry(main)> Book.where(price: 3218..4104)

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE `books`.`price` BETWEEN 3218 AND 4104

BETWEEN演算子を指定しています。

配列で指定

[7] pry(main)> Book.where(price: [3218, 4104])

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE `books`.`price` IN (3218, 4104)

プレイスホルダーで指定(名前なしパラメーター)

[7] pry(main)> Book.where('title = ? and price >= ?', params[:title], params[:price])

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE (title = 'プログラミング言語Ruby' and price >= 4104)

名前なしパラメーターで指定する時の、メリットは、記述がシンプルであることです。
デメリットには、パラメータと値の対応関係が分かりにくかったり、パラメーターの増減や順番の変化に影響を受けやすいということがあります。

プレイスホルダーで指定(名前付きパラメーター)

[8] pry(main)> Book.where('title = :title and price >= :price', title: params[:title], price: params[:price])

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE (title = 'プログラミング言語Ruby' and price >= 4104)

名前付きパラメーターで指定する時の、メリットは、パラメータと値の対応が分かりやすいことです。
また、デメリットは、記述がやや冗長であることです。

not

否定条件句を設定

[9] pry(main)> Book.where.not(price: params[:price])

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE `books`.`price` != 4104

SQLでは指定の条件を「!=」演算子で反転しています。

or

論理和で結合

[10] pry(main)> Book.where(title: '基礎 Ruby on Rails').or(Book.where('price > 3000'))

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE (`books`.`title` = '基礎 Ruby on Rails' OR (price > 3000))

結合対象の両者は、同じモデルで、where条件式だけが異なるActiveRecord::Relationオブジェクトでなければなりません

order

ソート式で指定

[11] pry(main)> Book.where('price >= 2000').order(price: :desc)

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE (price >= 2000) ORDER BY `books`.`price` DESC

取得したデータを特定のキーで並べ替えます。

reorder

ソート式を上書きして指定

[12] pry(main)> Book.where('price >= 2000').order(price: :asc).reorder(price: :desc)

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` WHERE (price >= 2000) ORDER BY `books`.`price` DESC

前のorderメソッドを無視してreorderでのソート順となります。

前のソート式を打ち消し指定

[13] pry(main)> Book.order(:price).reorder(nil)

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books`

select

取得列を明示的に指定

[14] pry(main)> Book.where('price >= 2000').select(:id, :title, :price)

これにより次のSQLが発行されます。

SELECT `books`.`id`, `books`.`title`, `books`.`price` FROM `books` WHERE (price >= 2000)

注意点として、selectメソッドで取得列を限定した場合、取得していない列(プロパティ)にアクセスしようとすると、ActiveRecord::MissingAttributeErrorが発生することがあげられます。

distinct

重複のないレコードを指定

[15] pry(main)> Book.select(:id, :title, :price).distinct.order(:price)

これにより次のSQLが発行されます。

SELECT DISTINCT `books`.`id`, `books`.`title`, `books`.`price` FROM `books` ORDER BY `books`.`price` ASC

結果セットから重複した行を除去します。
distinct(false)をプラスで指定すると、一度追加したDISTINCT句を破棄することができます。

limit, offset

特定範囲のレコードだけを取得

[16] pry(main)> Book.order(price: :desc).limit(3).offset(6)

これにより次のSQLが発行されます。

SELECT  `books`.* FROM `books` ORDER BY `books`.`price` DESC LIMIT 3 OFFSET 6

limitで取得件数を指定し、offsetで取得開始位置を指定します。
上記の例では、「6件目から最大3件取得」と意味します。

first, last

先頭/末尾のレコードを取得

[17] pry(main)> Book.order(price: :asc).first

これにより次のSQLが発行されます。

SELECT  `books`.* FROM `books` ORDER BY `books`.`price` ASC LIMIT 1

first, lastメソッドはクエリメソッドではないため、遅延ロードの対象外です。
メソッドチェーンの途中で記述することができないことに注意します。

group

特定のキー指定でグループ化

[18] pry(main)> Book.select(:id, :title, :price).group(:title)

これにより次のSQLが発行されます。

SELECT `books`.`id`, `books`.`title`, `books`.`price` FROM `books` GROUP BY `books`.`title`

データを集計する際などに利用します。

having

更にデータを絞り込んで取得

[19] pry(main)> Book.select(:id, :title, :price).group(:title).having('price >= 3000')

これにより次のSQLが発行されます。

SELECT `books`.`id`, `books`.`title`, `books`.`price` FROM `books` GROUP BY `books`.`title` HAVING (price >= 3000)

havingメソッドを利用することで、集計した結果をもとに、更にデータを絞り込むことが可能です。

unscope

条件式を除去

[20] pry(main)> Book.where(title: "プログラミング言語Ruby").order(:price).select(:id, :title, :price).unscope(:where, :select)

これにより次のSQLが発行されます。

SELECT `books`.* FROM `books` ORDER BY `books`.`price` ASC

クエリメソッドは、条件式を積み重ねて、最終的に結果が必要になってはじめてクエリが実行される(遅延ロード)ため、実行する前であれば、一度追加した条件式を取り消すことができます。

none

空の結果セットを取得

[21] pry(main)> Book.none

これにより次のように空の結果セットを取得できます。

=> []

noneメソッドを利用すると、戻り値はRelatioin(NullRelation)オブジェクトとなります。この場合、中身が空なだけでそれ自体は結果セットなので、eachメソッドを利用した際にもエラーになることはありません。

まとめ

プログラミングはつまるところ、データの流れの制御だと認識しておりますので、その根幹をなすデータベースとのやりとりをするモデルを理解することが、アプリケーションへの絶対的な理解に繋がりると思います。

今回はその一要素であるクエリメソッドをまとめることにしました。

少しでも役に立ったという方は、いいね、お願いします(^^)

Rails関連記事

正規表現まとめ(基礎)[Ruby編]
配列で利用できる主なメソッドをまとめてみた[Ruby編]
Mysql2::Error: Duplicate entry for key.. エラーを撃退した話(validationの設定)
Rails5でJqueryを利用しようとして少しハマった件(Uncaught ReferenceError: $ is not defined)
change_columnでの設定はrollbackできない話(This migration uses change_column, which is not automatically reversible.)[Rails: migration]
sessionに保存されたHashを別アクションで利用しようとした際にデータ型の変更によりハマった件[Rails]
Railsでfacebookログイン認証機能を実装してみた + エラーの原因はバリデーションだった
csvファイルを作成し、初期データを挿入しよう[Rails]
ArgumentError in ********Controller#send [Rails]
Render and/or redirect were called multiple times in this action...の罠[Rails]

59
74
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
74