Active Record入門:検索・更新・リレーションの基本まとめ
はじめに
Active RecordはRailsのORMで、Rubyのコードでデータベースを操作できます。SQLを直接書かなくても User.where(name: "田中") のように書けば、対応するSQLが自動で発行されます。
この記事では基本的なCRUD操作から、検索・リレーション・スコープまでまとめます。
セットアップ
モデルクラスは ApplicationRecord を継承して作ります。
# app/models/user.rb class User < ApplicationRecord end
Railsはテーブル名を自動で推測します。User モデルは users テーブルに対応します。
bin/rails generate model User name:string email:string bin/rails db:migrate
基本のCRUD
Create — 作成
# new + save user = User.new(name: "田中太郎", email: "tanaka@example.com") user.save # => true / false # create(new + save を一括) user = User.create(name: "田中太郎", email: "tanaka@example.com") # create!(失敗時に例外を発生) user = User.create!(name: "田中太郎", email: "tanaka@example.com")
発行されるSQL:
INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES ('田中太郎', 'tanaka@example.com', '2026-05-29', '2026-05-29')
Read — 検索
User.all # 全件 User.find(1) # IDで1件(なければ例外) User.find_by(name: "田中太郎") # 条件で1件(なければnil) User.find_by!(name: "田中太郎") # 条件で1件(なければ例外) User.where(name: "田中太郎") # 条件で複数件(Relation) User.first # 最初の1件 User.last # 最後の1件 User.count # 件数
Update — 更新
user = User.find(1) # update user.update(name: "田中次郎") # => true / false user.update!(name: "田中次郎") # 失敗時に例外 # 属性を変更してsave user.name = "田中次郎" user.save # 一括更新(バリデーション・コールバックをスキップ) User.where(active: false).update_all(deleted_at: Time.current)
発行されるSQL:
UPDATE "users" SET "name" = '田中次郎', "updated_at" = '2026-05-29' WHERE "id" = 1
Delete — 削除
user = User.find(1) user.destroy # コールバックあり user.delete # コールバックなし・SQLを直接発行 # 一括削除 User.where(active: false).destroy_all # コールバックあり(N回SQL発行) User.where(active: false).delete_all # コールバックなし(1回のSQL)
検索
where
User.where(name: "田中太郎") User.where(name: ["田中太郎", "鈴木花子"]) # IN句 User.where.not(name: "田中太郎") # NOT User.where("created_at > ?", 1.week.ago) # 文字列条件(プレースホルダ必須) User.where("name LIKE ?", "%田中%") User.where(active: true).where("age > ?", 20) # AND条件(チェーン)
順序・件数
User.order(:name) # 昇順 User.order(created_at: :desc) # 降順 User.order("created_at DESC, name ASC") User.limit(10) User.limit(10).offset(20) # ページネーション
集計
User.count User.where(active: true).count User.maximum(:age) User.minimum(:age) User.average(:age) User.sum(:point) User.group(:role).count # GROUP BY role
存在確認
User.exists?(1) # IDで確認 User.exists?(name: "田中太郎") # 条件で確認 User.where(active: true).exists? User.where(active: true).none? # 0件かどうか
select・pluck
# select はActiveRecordオブジェクトを返す User.select(:id, :name) # pluck は値の配列を返す(軽量) User.pluck(:name) # => ["田中太郎", "鈴木花子"] User.pluck(:id, :name) # => [[1, "田中太郎"], [2, "鈴木花子"]]
リレーション
アソシエーションの定義
class User < ApplicationRecord has_many :posts has_many :comments, through: :posts belongs_to :team, optional: true has_one :profile end class Post < ApplicationRecord belongs_to :user has_many :comments end
アソシエーションの使い方
user = User.find(1) user.posts # そのユーザーの投稿一覧 user.posts.count user.posts.create(title: "新しい記事") user.posts.where(published: true) post = Post.find(1) post.user # 投稿者 post.user.name
N+1問題と includes
# N+1が発生するコード(ユーザーごとにSQLが発行される) User.all.each do |user| puts user.posts.count # ← ここでSQLが都度発行される end # includes で一括取得(2回のSQLで済む) User.includes(:posts).each do |user| puts user.posts.count end
発行されるSQL(includes使用時):
SELECT * FROM "users" SELECT * FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3, ...)
joins
# INNER JOIN(関連レコードがあるユーザーだけ取得) User.joins(:posts) User.joins(:posts).where(posts: { published: true }) # LEFT JOIN(関連がなくても取得) User.left_joins(:posts)
スコープ
よく使う検索条件をスコープとして定義しておくと再利用できます。
class User < ApplicationRecord scope :active, -> { where(active: true) } scope :recent, -> { order(created_at: :desc) } scope :by_role, ->(role) { where(role: role) } end
User.active User.active.recent User.by_role("admin") User.active.by_role("admin").limit(10)
バリデーション
class User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP } end
バリデーションは save・update・create 時に自動で実行されます。
user = User.new(name: "") user.valid? # => false user.errors[:name] # => ["can't be blank"] user.errors.full_messages # => ["Name can't be blank"]
バリデーションをスキップして保存したい場合:
user.save(validate: false)
コールバック
class User < ApplicationRecord before_save :normalize_email after_create :send_welcome_email private def normalize_email self.email = email.downcase.strip end def send_welcome_email UserMailer.welcome(self).deliver_later end end
主なコールバック:
| タイミング | コールバック |
|---|---|
| 保存前 | before_validation, before_save, before_create, before_update |
| 保存後 | after_save, after_create, after_update, after_commit |
| 削除前後 | before_destroy, after_destroy |
SQLを確認する
発行されるSQLはRailsコンソールで確認できます。
bin/rails console
User.where(active: true).to_sql # => "SELECT \"users\".* FROM \"users\" WHERE \"users\".\"active\" = 1" User.includes(:posts).explain # => EXPLAIN for: SELECT ...(実行計画)
開発環境ではログ(log/development.log)にも全SQLが出力されます。
DBに直接ログインして確認する方法は「SQLiteチートシート:ログインからスキーマ確認・DMLまとめ」「MySQLチートシート:ログインからスキーマ確認・DMLまとめ」「PostgreSQLチートシート:ログインからスキーマ確認・DMLまとめ」を参照してください。
まとめ
| 操作 | メソッド |
|---|---|
| 全件取得 | Model.all |
| IDで1件 | Model.find(id) |
| 条件で1件 | Model.find_by(条件) |
| 条件で複数 | Model.where(条件) |
| 作成 | Model.create(属性) |
| 更新 | record.update(属性) |
| 削除 | record.destroy |
| N+1対策 | Model.includes(:関連名) |
RailsのDBマイグレーション操作は「Rails のDB操作コマンド入門:db:create から db:migrate まで」を参照してください。
モデルのテストの書き方は「RSpec入門:インストールからモデルスペックの書き方まで」を参照してください。