1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rails ActiveRecordのクエリガイド

Posted at

テーブル構造

まず、以下のようなテーブル構造を例に説明します:

usersテーブル

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255),
  email VARCHAR(255),
  age INTEGER,
  premium BOOLEAN,
  created_at TIMESTAMP
);

postsテーブル

CREATE TABLE posts (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id),
  title VARCHAR(255),
  content TEXT,
  likes_count INTEGER DEFAULT 0,
  created_at TIMESTAMP
);

commentsテーブル

CREATE TABLE comments (
  id SERIAL PRIMARY KEY,
  post_id INTEGER REFERENCES posts(id),
  user_id INTEGER REFERENCES users(id),
  content TEXT,
  created_at TIMESTAMP
);

サンプルデータ

usersテーブルのデータ

INSERT INTO users (id, name, email, age, premium, created_at) VALUES
(1, '山田太郎', 'yamada@example.com', 25, true, '2024-01-01 10:00:00'),
(2, '佐藤花子', 'sato@example.com', 30, false, '2024-01-02 11:00:00'),
(3, '鈴木一郎', 'suzuki@example.com', 20, true, '2024-01-03 12:00:00'),
(4, '田中次郎', 'tanaka@example.com', 35, false, '2024-01-04 13:00:00');

postsテーブルのデータ

INSERT INTO posts (id, user_id, title, content, likes_count, created_at) VALUES
(1, 1, '初めての投稿', 'こんにちは!', 5, '2024-01-01 10:30:00'),
(2, 1, '2番目の投稿', 'よろしくお願いします', 10, '2024-01-02 10:30:00'),
(3, 2, '私の投稿', 'はじめまして!', 3, '2024-01-02 12:30:00'),
(4, 3, 'テスト投稿', 'テストです', 15, '2024-01-03 14:30:00');

commentsテーブルのデータ

INSERT INTO comments (id, post_id, user_id, content, created_at) VALUES
(1, 1, 2, 'いいね!', '2024-01-01 11:00:00'),
(2, 1, 3, '素晴らしい!', '2024-01-01 12:00:00'),
(3, 2, 4, '参考になりました', '2024-01-02 13:00:00'),
(4, 3, 1, 'ありがとう!', '2024-01-02 14:00:00');

クエリ例と実行結果

1. 基本的な検索

主キーでの検索

# ActiveRecord
user = User.find(1)

# 生成されるSQL
SELECT * FROM users WHERE id = 1;

# 結果
# {
#   id: 1,
#   name: "山田太郎",
#   email: "yamada@example.com",
#   age: 25,
#   premium: true,
#   created_at: "2024-01-01 10:00:00"
# }

条件での検索

# ActiveRecord
users = User.where(age: 20..30)

# 生成されるSQL
SELECT * FROM users WHERE age BETWEEN 20 AND 30;

# 結果
# [
#   {
#     id: 1,
#     name: "山田太郎",
#     age: 25,
#     ...
#   },
#   {
#     id: 3,
#     name: "鈴木一郎",
#     age: 20,
#     ...
#   }
# ]

2. 関連テーブルとの結合

ユーザーと投稿を結合

# ActiveRecord
posts_with_users = Post.joins(:user).select('posts.*, users.name as author_name')

# 生成されるSQL
SELECT posts.*, users.name as author_name 
FROM posts 
INNER JOIN users ON users.id = posts.user_id;

# 結果
# [
#   {
#     id: 1,
#     title: "初めての投稿",
#     content: "こんにちは!",
#     author_name: "山田太郎",
#     ...
#   },
#   ...
# ]

3. 集計とグループ化

ユーザーごとの投稿数

# ActiveRecord
post_counts = Post.group(:user_id).count

# 生成されるSQL
SELECT user_id, COUNT(*) as count 
FROM posts 
GROUP BY user_id;

# 結果
# {
#   1 => 2,  # 山田太郎さんの投稿数
#   2 => 1,  # 佐藤花子さんの投稿数
#   3 => 1   # 鈴木一郎さんの投稿数
# }

4. 複雑な条件

いいねが5件以上で、コメントがついている投稿を検索

# ActiveRecord
popular_posts = Post.joins(:comments)
                   .where('posts.likes_count >= ?', 5)
                   .distinct

# 生成されるSQL
SELECT DISTINCT posts.* 
FROM posts 
INNER JOIN comments ON comments.post_id = posts.id 
WHERE posts.likes_count >= 5;

# 結果
# [
#   {
#     id: 1,
#     title: "初めての投稿",
#     likes_count: 5,
#     ...
#   },
#   {
#     id: 2,
#     title: "2番目の投稿",
#     likes_count: 10,
#     ...
#   }
# ]

5. Eagerロードの例

投稿とそのコメントを一度に取得

# ActiveRecord
posts = Post.includes(:comments).where(user_id: 1)

# 生成されるSQL
SELECT * FROM posts WHERE user_id = 1;
SELECT * FROM comments WHERE post_id IN (1, 2);

# 結果
# [
#   {
#     id: 1,
#     title: "初めての投稿",
#     comments: [
#       {
#         id: 1,
#         content: "いいね!",
#         ...
#       },
#       {
#         id: 2,
#         content: "素晴らしい!",
#         ...
#       }
#     ]
#   },
#   {
#     id: 2,
#     title: "2番目の投稿",
#     comments: [
#       {
#         id: 3,
#         content: "参考になりました",
#         ...
#       }
#     ]
#   }
# ]

6. スコープの実践例

class Post < ApplicationRecord
  # 人気の投稿を取得するスコープ
  scope :popular, -> { where('likes_count >= ?', 5) }
  
  # 特定の期間の投稿を取得するスコープ
  scope :recent, -> { where('created_at >= ?', 1.week.ago) }
end

# ActiveRecord
popular_recent_posts = Post.popular.recent

# 生成されるSQL
SELECT * FROM posts 
WHERE likes_count >= 5 
AND created_at >= '2024-01-24 00:00:00';

# 結果
# [
#   {
#     id: 1,
#     title: "初めての投稿",
#     likes_count: 5,
#     ...
#   },
#   {
#     id: 4,
#     title: "テスト投稿",
#     likes_count: 15,
#     ...
#   }
# ]

パフォーマンス最適化のポイント

  1. インデックスの活用
# マイグレーションファイル
class AddIndexesToPosts < ActiveRecord::Migration[7.0]
  def change
    add_index :posts, :user_id  # 外部キーには必ずインデックスを
    add_index :posts, :likes_count  # 検索条件によく使うカラムにインデックスを
    add_index :posts, [:user_id, :created_at]  # 複合インデックス
  end
end
  1. N+1問題の回避
# 悪い例(N+1問題が発生)
posts = Post.all
posts.each { |post| puts post.user.name }  # 各投稿ごとにSQLが実行される

# 良い例(Eagerロードを使用)
posts = Post.includes(:user)
posts.each { |post| puts post.user.name }  # SQLは2回だけ実行される
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?