1. 結論(この記事で得られること)
正直に白状すると、私も「「pluck(:id)」と「ids」って同じでしょ?」と思っていた時期がありました。実際に計測してみると、予想とは全く違う結果が出てきて驚いたんです。
この記事で得られること:
- pluck(:id)、ids、select(:id)の実際のパフォーマンス差
- メモリ使用量とSQL発行回数の違い
- 実務で使い分けるべき判断基準
- AI時代の効率的な計測・検証方法
結論から言うと、レコード数が多い場合はidsが圧倒的に有利ですが、思わぬ落とし穴もありました。
2. 前提(環境・読者層)
想定読者:
- Rails歴1年以上のエンジニア
- ActiveRecordの基本は理解している
- パフォーマンス改善に興味がある方
検証環境:
# Gemfile
gem 'rails', '~> 7.0.0'
gem 'pg', '~> 1.1'
# 環境
Ruby 3.2.0
PostgreSQL 14
AWS RDS(db.t3.micro)
3. Before:よくあるつまずきポイント
実務でよく見かける、何気なく書いているコードたちです。
# パターン1:pluck使用
user_ids = User.where(active: true).pluck(:id)
orders = Order.where(user_id: user_ids)
# パターン2:ids使用
user_ids = User.where(active: true).ids
orders = Order.where(user_id: user_ids)
# パターン3:select + map使用
user_ids = User.where(active: true).select(:id).map(&:id)
よくある認識:
- 「どれも同じ結果だから、書きやすいものを使えばいい」
- 「pluckが一番軽そう」
- 「idsは内部的にpluck(:id)を呼んでるだけでしょ?」
実際に私のチームでもレビューで議論になることがありました。「なんとなく」で選択していたんですよね。
4. After:基本的な解決パターン
まず、それぞれの動作を正確に理解しましょう。
# 1. pluck(:id) - カラムの値だけを取得
User.where(active: true).pluck(:id)
# SQL: SELECT "users"."id" FROM "users" WHERE "users"."active" = true
# 戻り値: [1, 2, 3, 4, 5]
# 2. ids - ActiveRecordが提供する最適化されたメソッド
User.where(active: true).ids
# SQL: SELECT "users"."id" FROM "users" WHERE "users"."active" = true
# 戻り値: [1, 2, 3, 4, 5]
# 内部的には pluck(primary_key) を呼ぶが、最適化あり
# 3. select + map - オブジェクト経由でアクセス
User.where(active: true).select(:id).map(&:id)
# SQL: SELECT "users"."id" FROM "users" WHERE "users"."active" = true
# 戻り値: [1, 2, 3, 4, 5]
# ただし、内部的にはUserオブジェクトを生成
基本的な使い分け:
# 大量データ(1万件以上)→ ids
large_user_ids = User.where(created_at: 1.year.ago..).ids
# 複数カラム必要 → pluck
user_data = User.where(active: true).pluck(:id, :email, :name)
# 後続でActiveRecordオブジェクトも使う → select
users = User.where(active: true).select(:id, :email)
user_ids = users.map(&:id)
emails = users.map(&:email)