26
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

ActiveRecord CRUDサンプル

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のような)

普通に書くとこんな感じ。

ruby
Blog.where("? <= id", 2).each do |b|
  do_something
end

eachが始まるところで結果全てを取得するので、大量データには対応出来ないこともある。
というわけで、そんなときには内部的に何回かに分けて取得してくれる find_each を使う。

ruby
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が走っているだけ。

SQL
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 クエリインターフェイス

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
26
Help us understand the problem. What are the problem?