Rails ActiveRecordのクエリガイド

## テーブル構造

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

### usersテーブル
```sql
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255),
  email VARCHAR(255),
  age INTEGER,
  premium BOOLEAN,
  created_at TIMESTAMP
);
```

### postsテーブル
```sql
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テーブル
```sql
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テーブルのデータ
```sql
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テーブルのデータ
```sql
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テーブルのデータ
```sql
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. 基本的な検索

#### 主キーでの検索
```ruby
# 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"
# }
```

#### 条件での検索
```ruby
# 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. 関連テーブルとの結合

#### ユーザーと投稿を結合
```ruby
# 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. 集計とグループ化

#### ユーザーごとの投稿数
```ruby
# 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件以上で、コメントがついている投稿を検索
```ruby
# 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ロードの例

#### 投稿とそのコメントを一度に取得
```ruby
# 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. スコープの実践例

```ruby
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. **インデックスの活用**
```ruby
# マイグレーションファイル
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
```

2. **N+1問題の回避**
```ruby
# 悪い例（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回だけ実行される
```