3.6 クエリ

単一のオブジェクトを取り出す

findメソッド

findメソッドは指定した引数と同じプライマリキーを持つオブジェクトを取得します。どのレコードにもマッチしない場合はActiveRecord::RecordNotFound例外が発生します。

user = User.find(1)
=> #<User id: 1, email: nil, password: nil, created_at: "2018-06-26 15:52:29", updated_at: "2018-06-26 15:52:29">

find_byメソッド

find_byメソッドは指定条件にマッチする最初のオブジェクトを取得します。

user = User.find_by(id: 1)
=> #<User id: 1, email: nil, password: nil, created_at: "2018-06-26 15:52:29", updated_at: "2018-06-26 15:52:29">

user = User.find_by(email: '[email protected]')
=> nil

firstメソッド

firstメソッドはプライマリキー順の最初のオブジェクトを取得します。オブジェクトが存在しない場合はActiveRecord::RecordNotFound例外が発生します。

user = User.first
=> #<User id: 1, email: nil, password: nil, created_at: "2018-06-26 15:52:29", updated_at: "2018-06-26 15:52:29">

lastメソッド

lastメソッドはプライマリキー順の最後のオブジェクトを取得します。オブジェクトが存在しない場合はActiveRecord::RecordNotFound例外が発生します。

user = User.last
=> #<User id: 1, email: nil, password: nil, created_at: "2018-06-26 15:52:29", updated_at: "2018-06-26 15:52:29">

takeメソッド

takeメソッドはデータベースから無条件にオブジェクトを取得します。オブジェクトが存在しない場合はActiveRecord::RecordNotFound例外が発生します。

user = User.take
=> #<User id: 1, email: nil, password: nil, created_at: "2018-06-26 15:52:29", updated_at: "2018-06-26 15:52:29">

複数のオブジェクトを取り出す

find_eachメソッド

find_eachメソッドはデータベースからレコードを一つずつ取り出し、ブロックに渡します。 ブロックに渡されたレコードを使って様々な処理をすることができます。

以下の例では、全ユーザーのemailを出力しています。

User.find_each do |user|
  p user.email
end

find_in_batchesメソッド

find_in_batchesメソッドはデータベースからレコードをデフォルトで1000件ずつ取り出し、ブロックに渡します。 ブロックに渡されたレコードの配列を使って様々な処理をすることができます。

以下の例では、全ユーザーのemailを出力しています。

User.find_in_batches do |users|
  export_to_csv(users) # csvに1000件ずつエクスポート
end

条件を指定して取り出す

文字列で条件を指定する

条件を指定するにはwhereメソッドを使います。whereメソッドに条件の文字列を渡すことで検索が可能です。

Post.where("title = 'This is title'")
=> #<ActiveRecord::Relation [#<Post id: 1, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 2, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>]>

配列で条件を指定する

whereメソッドは配列で条件を指定することもできます。この場合条件文内の?whereメソッドの条件文の後の引数で置き換えられます。

condition = 'This is title'
Post.where("title = ?", condition)
=> #<ActiveRecord::Relation [#<Post id: 1, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 2, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>]>

ハッシュで条件を指定する

whereメソッドはハッシュで条件を指定することもできます。この場合ハッシュのキーはカラム名、値は条件になります。

Post.where(title: 'This is title')
=> #<ActiveRecord::Relation [#<Post id: 1, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 2, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>]>

NOT条件

SQLのNOTを表現するにはwhere.notを使います。NOTの条件はnotの引き数に指定します。

Post.where.not(title: '') # タイトルが空ではない
=> #<ActiveRecord::Relation [#<Post id: 1, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 2, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 4, title: "これはタイトルです", body: "これは本文です。", created_at: "2018-06-27 08:36:22", updated_at: "2018-06-27 08:36:22", published_at: nil, user_id: nil>, #<Post id: 5, title: "カテゴリの投稿", body: nil, created_at: "2018-06-27 08:53:18", updated_at: "2018-06-27 08:53:18", published_at: nil, user_id: nil>]>

OR条件

OR条件を使いたい場合は、1つ目のリレーションでorメソッドを呼び出し、そのメソッドの引数に2つ目のリレーションを渡すことで実現できます。

SQLのORを表現するにはorメソッドを使います。一つ目の条件のあとでorを呼び出し、その引数に二つ目の条件を渡します。

Post.where(title: 'This is title').or(Post.where(title: 'これはタイトルです'))
=> #<ActiveRecord::Relation [#<Post id: 1, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 2, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 4, title: "これはタイトルです", body: "これは本文です。", created_at: "2018-06-27 08:36:22", updated_at: "2018-06-27 08:36:22", published_at: nil, user_id: nil>]>

並び順を指定して取り出す

orderメソッドでレコードを並び替えることができます。

降順で並び替え

Post.order(created_at: :desc)

=> #<ActiveRecord::Relation [#<Post id: 5, title: "カテゴリの投稿", body: nil, created_at: "2018-06-27 08:53:18", updated_at: "2018-06-27 08:53:18", published_at: nil, user_id: nil>, #<Post id: 4, title: "これはタイトルです", body: "これは本文です。", created_at: "2018-06-27 08:36:22", updated_at: "2018-06-27 08:36:22", published_at: nil, user_id: nil>, #<Post id: 3, title: nil, body: nil, created_at: "2018-06-25 05:23:58", updated_at: "2018-06-25 05:23:58", published_at: nil, user_id: nil>, #<Post id: 1, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 2, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>]>

昇順で並び替え

Post.order(created_at: :asc)

=> #<ActiveRecord::Relation [#<Post id: 1, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 2, title: "This is title", body: nil, created_at: "2018-06-15 15:00:50", updated_at: "2018-06-15 15:00:50", published_at: nil, user_id: nil>, #<Post id: 3, title: nil, body: nil, created_at: "2018-06-25 05:23:58", updated_at: "2018-06-25 05:23:58", published_at: nil, user_id: nil>, #<Post id: 4, title: "これはタイトルです", body: "これは本文です。", created_at: "2018-06-27 08:36:22", updated_at: "2018-06-27 08:36:22", published_at: nil, user_id: nil>, #<Post id: 5, title: "カテゴリの投稿", body: nil, created_at: "2018-06-27 08:53:18", updated_at: "2018-06-27 08:53:18", published_at: nil, user_id: nil>]>

特定のカラムのみを取り出す

selectメソッドで取得するレコードのカラムを指定することができます。

例えばidtitleのみを取得したい場合は以下のようになります。

Post.select('id, title')
=> #<ActiveRecord::Relation [#<Post id: 1, title: "This is title">, #<Post id: 2, title: "This is title">, #<Post id: 3, title: nil>, #<Post id: 4, title: "これはタイトルです">, #<Post id: 5, title: "カテゴリの投稿">]>

件数、範囲を指定して取り出す

limitおよびoffsetメソッドを利用することで、SQLのLIMIT,OFFSETを表現することができます。

件数を指定する

User.limit(3)
=> #<ActiveRecord::Relation [#<User id: 1, email: nil, password: nil, created_at: "2018-06-26 15:52:29", updated_at: "2018-06-26 15:52:29">, #<User id: 2, email: "[email protected]", password: nil, created_at: "2018-07-01 09:06:41", updated_at: "2018-07-01 09:06:41">, #<User id: 3, email: "[email protected]", password: nil, created_at: "2018-07-01 09:06:41", updated_at: "2018-07-01 09:06:41">]>

範囲を指定する

User.limit(3).offset(3)
=> #<ActiveRecord::Relation [#<User id: 4, email: "[email protected]", password: nil, created_at: "2018-07-01 09:06:41", updated_at: "2018-07-01 09:06:41">, #<User id: 5, email: "[email protected]", password: nil, created_at: "2018-07-01 09:06:41", updated_at: "2018-07-01 09:06:41">, #<User id: 6, email: "[email protected]", password: nil, created_at: "2018-07-01 09:06:41", updated_at: "2018-07-01 09:06:41">]>

グルーピングして取り出す

groupメソッドを利用することで、SQLのGROUP BYを利用することができます。

たとえば、Post作成日のごとに検索したい場合は、以下のようにします。

Post.group("date(published_at)")
=> #<ActiveRecord::Relation [#<Post id: 5, title: "カテゴリの投稿", body: nil, created_at: "2018-06-27 08:53:18", updated_at: "2018-06-27 08:53:18", published_at: nil, user_id: nil>, #<Post id: 305, title: "title 99", body: "body 99", created_at: "2018-07-01 10:35:11", updated_at: "2018-07-01 10:35:11", published_at: "2018-03-24 10:35:11", user_id: nil>, #<Post id: 304, title: "title 98", body: "body 98", created_at: "2018-07-01 10:35:11", updated_at: "2018-07-01 10:35:11", published_at: "2018-03-25 10:35:11", user_id: nil>, #<Post id: 303, title: "title 97", body: "body 97", created_at: "2018-07-01 10:35:10", updated_at: "2018-07-01 10:35:10", published_at: "2018-03-26 10:35:10", user_id: nil>, #<Post id: 302, title: "title 96", body: "body 96", created_at: "2018-07-01 10:35:10", updated_at: "2018-07-01 10:35:10", published_at: "2018-03-27 10:35:10", user_id: nil>, #<Post id: 301, title: "title 95", body: "body 95", created_at: "2018-07-01 10:35:10", updated_at: "2018-07-01 10:35:10", published_at: "2018-03-28 10:35:10", user_id: nil>, #<Post id: 300, title: "title 94", body: "body 94", created_at: "2018-07-01 10:35:10", updated_at: "2018-07-01 10:35:10", published_at: "2018-03-29 10:35:10", user_id: nil>, #<Post id: 299, title: "title 93", body: "body 93", created_at: "2018-07-01 10:35:10", updated_at: "2018-07-01 10:35:10", published_at: "2018-03-30 10:35:10", user_id: nil>, #<Post id: 298, title: "title 92", body: "body 92", created_at: "2018-07-01 10:35:10", updated_at: "2018-07-01 10:35:10", published_at: "2018-03-31 10:35:10", user_id: nil>, #<Post id: 297, title: "title 91", body: "body 91", created_at: "2018-07-01 10:35:10", updated_at: "2018-07-01 10:35:10", published_at: "2018-04-01 10:35:10", user_id: nil>, ...]>

Post.group("date(published_at)").count # 作成日ごとの件数
=> {nil=>5, "2018-03-24"=>3, "2018-03-25"=>3, "2018-03-26"=>3, "2018-03-27"=>3, "2018-03-28"=>3, "2018-03-29"=>3, "2018-03-30"=>3, "2018-03-31"=>3, "2018-04-01"=>3, "2018-04-02"=>3, "2018-04-03"=>3, "2018-04-04"=>3, "2018-04-05"=>3, "2018-04-06"=>3, "2018-04-07"=>3, "2018-04-08"=>3, "2018-04-09"=>3, "2018-04-10"=>3, "2018-04-11"=>3, "2018-04-12"=>3, "2018-04-13"=>3, "2018-04-14"=>3, "2018-04-15"=>3, "2018-04-16"=>3, "2018-04-17"=>3, "2018-04-18"=>3, "2018-04-19"=>3, "2018-04-20"=>3, "2018-04-21"=>3, "2018-04-22"=>3, "2018-04-23"=>3, "2018-04-24"=>3, "2018-04-25"=>3, "2018-04-26"=>3, "2018-04-27"=>3, "2018-04-28"=>3, "2018-04-29"=>3, "2018-04-30"=>3, "2018-05-01"=>3, "2018-05-02"=>3, "2018-05-03"=>3, "2018-05-04"=>3, "2018-05-05"=>3, "2018-05-06"=>3, "2018-05-07"=>3, "2018-05-08"=>3, "2018-05-09"=>3, "2018-05-10"=>3, "2018-05-11"=>3, "2018-05-12"=>3, "2018-05-13"=>3, "2018-05-14"=>3, "2018-05-15"=>3, "2018-05-16"=>3, "2018-05-17"=>3, "2018-05-18"=>3, "2018-05-19"=>3, "2018-05-20"=>3, "2018-05-21"=>3, "2018-05-22"=>3, "2018-05-23"=>3, "2018-05-24"=>3, "2018-05-25"=>3, "2018-05-26"=>3, "2018-05-27"=>3, "2018-05-28"=>3, "2018-05-29"=>3, "2018-05-30"=>3, "2018-05-31"=>3, "2018-06-01"=>3, "2018-06-02"=>3, "2018-06-03"=>3, "2018-06-04"=>3, "2018-06-05"=>3, "2018-06-06"=>3, "2018-06-07"=>3, "2018-06-08"=>3, "2018-06-09"=>3, "2018-06-10"=>3, "2018-06-11"=>3, "2018-06-12"=>3, "2018-06-13"=>3, "2018-06-14"=>3, "2018-06-15"=>3, "2018-06-16"=>3, "2018-06-17"=>3, "2018-06-18"=>3, "2018-06-19"=>3, "2018-06-20"=>3, "2018-06-21"=>3, "2018-06-22"=>3, "2018-06-23"=>3, "2018-06-24"=>3, "2018-06-25"=>3, "2018-06-26"=>3, "2018-06-27"=>3, "2018-06-28"=>3, "2018-06-29"=>3, "2018-06-30"=>3, "2018-07-01"=>3}

テーブルを結合する

joinsメソッドおよびleft_outer_joinsメソッドを利用することで、SQLのJOIN句を表現することができます。

例えば以下のようにモデルが定義されています。

class User < ApplicationRecord
  has_many :posts
  has_many :comments
end

class Category < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :category
  belongs_to :user
  has_many :comments
end

class Comment < ApplicationRecord
  belongs_to :post
  belongs_to :user
end

一つのテーブルを結合する

Category.joins(:posts)

複数のテーブルを結合する

Post.joins(:cetegory, :comments)

階層化されたテーブルを結合する

Category.joins(posts: :comments)

結合されたテーブルで条件を指定する

配列および文字列を条件に使用する

Category.joins(:posts).where("posts.title = 'This is title'")
Category.joins(:posts).where('posts.title = ?', 'This is title')

ハッシュを条件に使用する

Category.joins(:posts).where(posts: { title: 'This is title' })

関連付けを一括読み込みする

includesメソッド利用することで関連づけされてモデルを一括で読み込むことができます。

Category.includes(:posts)

階層化されたモデルを一括読み込みする

Category.includes(posts: :comments)

スコープを使ってクエリを再利用する

スコープを利用することで、再利用したいクエリを登録しておくことができます。 スコープでは、whereorderjoinsなどのメソッドを使用でき、どのスコープを呼び出してもActiveRecord::Relationオブジェクトが返ります。 これにより、スコープを連鎖的に呼び出すことも可能です。

スコープの定義

class Post < ApplicationRecord
  scope :latest, -> { order(published_at: :desc) }
end

# 呼び出し方
Post.latest

スコープに引数を渡す

class Post < ApplicationRecord
  scope :published_before, ->(time) { where("published_at < ?", time) }
end

# 呼び出し方
Post.published_before(Date.yesterday)

スコープの条件

class Post < ApplicationRecord
  scope :published_before, ->(time) { where("published_at < ?", time) if time < Time.zone.now }
end

デフォルトのスコープを定義する

デフォルトで呼び出されるスコープを定義することができます。これはレコードが新規作成される時にも呼ばれることに注意してください。

class Post < ApplicationRecord
  default_scope { where(status: :published) }
end

Enumを利用する

ActiveRecordのEnumを使うには数値型のカラムを用意する必要があります。 ``

class Post < ApplicationRecord
  enum status: [:draft, :published, :rejected]
end

enumを定義するとRailsは自動的にスコープを生成します。

Post.draft # Post(status: :draft)と同じ
Post.published # Post(status: :published)と同じ
Post.rejected # Post(status: :rejected)と同じ

# またそれそれメソッドも追加される
book = Post.new(status: :available)

# draftかチェックするメソッド
book.draft? # => true

# publishedに変更するメソッド
book.published! # => true

# rejectedか確認するメソッド
book.rejected?   # => false

SQL文を利用する

ActiveRecordでSQL文を実行したい場合はexecuteメソッドを利用します。

row_sql = 'SELECT * FROM users'
ActiveRecord::Base.connection.execute(row_sql)

オブジェクトの存在を確認する

exists?メソッドを利用することでオブジェクトの存在確認をすることができます。

Post.exists?(1) # idを指定
Post.exists?(title: 'This is title') # ハッシュで条件を指定

レコード数をカウントする

レコードの数を数えるにはcountメソッドを利用します。

Post.count

フィールドの平均を計算する

テーブルにある特定のカラムの数値の平均を計算するには、averageメソッドを使います。

Post.average("page_views")

フィールドの合計を計算する

テーブルにある特定のカラムの数値の合計を計算するには、sumメソッドを使います。

Post.sum("page_views")

フィールドの最大値を取得する

テーブルにある特定のカラムの数値の最大値を取得するには、maximumメソッドを使います。

Post.maximum("page_views")

フィールドの最小値を取得する

テーブルにある特定のカラムの数値の最小値を取得するには、minimumメソッドを使います。

Post.minimum("page_views")

results matching ""

    No results matching ""