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
メソッドで取得するレコードのカラムを指定することができます。
例えばid
とtitle
のみを取得したい場合は以下のようになります。
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)
スコープを使ってクエリを再利用する
スコープを利用することで、再利用したいクエリを登録しておくことができます。
スコープでは、where
、order
、joins
などのメソッドを使用でき、どのスコープを呼び出しても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")