ActiveRecordのコードサンプル。
子テーブルの先読みやINNER JOIN, OUTER JOINなどについては こちらのページ にまとめた。
検索
Post.all
Post.first
Post.last
find系
# id = 1 LIMIT 1
Post.find(1) # 見つからなかったら ActiveRecord::RecordNotFound が throw される
# title = "title1" LIMIT 1
Post.find_by(:title, "title1") # 見つからなかったら nil が返る [Rails4以降]
Post.find_by_title("title1") # 同上
# title="title1" AND id=3 LIMIT 1
Post.find_by_title_and_id("title1", 3)
whereメソッド
# title="title1" AND id=1
Post.where(title: "title1", id: 1)
# 同上 ※プレースホルダータイプ
Post.where(:title = ? and id = ?", "title1", 1)
# 同上 ※シンボル指定
Post.where("title = :title and id = :id", {:title => "title1", :id => 1}
# 条件の否定(NOT)
# title != "title1"
Post.where.not(title: "title1")
範囲指定やLIKEなど
# title LIKE "title%"
Post.where("title LIKE ?", "title%")
# id <= 3 OR id = 5
Post.where("id <= 3 OR id = ?", 3, 5)
# id BETWEEN 1 AND 3
Post.where(id: 1..3)
# id IN (1, 3)
Post.where(id: [1, 3])
select
取得するカラムを絞り込む
Post.where(id: 3).select(:title, :boby)
pluck [Rails4]
指定列の配列を取得
Post.where(id: 1..3).pluck(:title, :body)
# =>
# [
# [ "t1", "b1"]
# [ "t2", "b2"]
# [ "t3", "b3"]
# ]
exists? [Rails4]
# SELECT 1 AS one FROM posts WHERE id = 10 LIMIT 1
Post.where(id: 10).exists?
# id=1 が存在する?
Post.exists?(1)
# 5000<price が存在する?
Book.exists?(['? < price', 5000])
# publish='テスト' が存在する
Book.exists?(publish: 'テスト')
# booksテーブルに1件でもデータが存在する?
Book.exists?
distinct
Post.select(:title).distinct
# distinctの破棄
Post.select(:title).distinct.distinct(false)
order/reorder
Post.where("title LIKE ?", "title%").order(updated_at: :desc, created_at: :asc)
# 前のorderを無視して(リセットして)、新しいorderをセットする
Post.order(updated_at: :desc).reorder(created_at: :desc)
# 前のorderをリセットするだけ
Post.order(updated_at: :desc).reorder(nil)
limit/offset
# SELECT * FROM posts ORDER BY title DESC LIMIT 3 OFFSET 4
Post.order(title: :desc).limit(3).offset(4)
group / having
# SELECT publish, AVG(price) AS avg_price
# FROM books
# GROUP BY publish
Book.select('publish, AVG(price) AS avg_price').group(:publish)
# SELECT publish, AVG(price) AS avg_price
# FROM books
# GROUP BY publish
# HAVING 2500 <= AVG(price)
Book.select('publish, AVG(price) AS avg_price').group(:publish).having('? <= AVG(price)', 2500)
生SQL(find_by_sql)
Books.find_by_sql("
SELECT publish, AVG(price) AS avg_price
FROM books
GROUP BY publish
HAVING 2500 <= AVG(price)
")
破壊的クエリーメソッド [Rails4]
where!, order!, select!, limit!, offset!, group!, having!, distinct! など、変数への再代入不要な破壊的メソッドが追加された。
条件式除去 [Rails4]
# where条件とselect条件を除去する例
Post.where("title LIKE ?", "title%").order(updated_at: :desc, created_at: :asc).select(:title, :body).unscope(:where, :select)
# where条件のcd列に対する条件を除去
Book.where(publish: 'テスト', cd: true).unscope(where: :cd)
空の結果を返す [Rails4]
Book.none
挿入系
new + save
post = Post.new(title: "xxx", body: "bbb")
post.save
# saveは結果がtrue/falseで返される
# save!とすると、失敗時に例外がthrowされる
post = Post.new
post.title = "xxx"
post.body = "bbb"
post.save
post = Post.new do |p|
p.title = "xxx"
p.body = "bbb"
end
post.save
create
new と save を一度に実行。
Post.create(title: "xxx", body: "bbb")
find_or_create_by [Rails4]
# Rails4
Post.find_or_create_by(title: "title6") do |post|
post.body = "hobby6"
end
# Rails3
Post.where(title: "title6").first_or_create do |post|
post.body = "hobby6"
end
更新系
post.title = "xxx"
post.save
post.update_attribute(:title, "xxx")
post.update_attributes(title: "xxx", body: "bbb")
Post.where(id: 1..3).update_all(title: "xxx", body: "bbb")
validationの有無などについてはこちらが詳しい。ActiveRecord の attribute 更新方法まとめ
その他更新系メソッド
メソッド | 概要 |
---|---|
post.new_record? | 未保存?(新規レコード?) |
post.persisted? | 上の反対 |
post.touch([name]) | update_at/on列を現在時刻で更新。name指定時はその列も。 |
post.changed? | 取得してから何らかの変更がされたか? |
post.destroyed? | 現在のオブジェクトが削除済みか? |
削除系
post.destroy
Post.destroy(1)
# データの削除のみ(アソシエーション・コールバックは無視される)
Post.delete(1)
Post.destroy_all(["title <> ?", "テスト"])
Post.where('title <> ?', 'テスト').destroy_all
トランザクション
begin
Post.transaction do
xxx
end
例外が発生しなかった場合の処理
rescue => e
例外が発生した場合の処理
end
その他
scope
class Post < ActiveRecord::Base
scope :top3, -> { order("created_at").limit(3) }
end
Post.top3
複数レコードに対する列挙処理(eachのような)
普通に書くとこんな感じ。
Blog.where("? <= id", 2).each do |b|
do_something
end
eachが始まるところで結果全てを取得するので、大量データには対応出来ないこともある。
というわけで、そんなときには内部的に何回かに分けて取得してくれる find_each
を使う。
Blog.where("? <= id", 2).find_each(batch_size: 2) do |b|
do_something
end
デフォルトでは1000件ずつ取得され(SELECTが走り)、1件ずつblockに渡される。
この例では、batch_size引数で1回に取得する件数も指定している。(2件)
その際に実行されたSQLがこれ。
Cursorを使っているわけではなく、単純に複数回SELECTが走っているだけ。
SELECT * FROM blogs WHERE (2 <= id)
ORDER BY id ASC LIMIT 2;
SELECT * FROM blogs WHERE (2 <= id) AND (id > 3)
ORDER BY id ASC LIMIT 2;
また、find_in_batches
メソッドは、1回のSELECTで取得したレコードを1つの配列としてblockに渡してくれる。
アソシエーション
種類
- belongs_to
- has_one
- has_many
- has_many :through
- has_one :through
- has_and_belongs_to_many
各アソシエーションを定義すると、様々な便利なメソッドが使えるようになる。そのあたりはRailsガイドに詳しく記載されている。
参考サイト・資料
ドットインストール - ActiveRecord入門
『Ruby on Rails4 アプリケーションプログラミング』翔泳社
RailsGuides - Active Record クエリインターフェイス