0
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】pluckとselectの使い分け

Posted at

はじめに

pluckselectの使いどころについて整理した学習記録です。どちらも特定カラムを取得するメソッドですが、返り値や使える機能、パフォーマンスに違いがあります。

比較のまとめ

項目 pluck select
返り値の型 配列(Array) ActiveRecord::Relation
モデルメソッド利用 不可 可能
メモリ効率 高い(軽量) やや低い(オブジェクト生成あり)
サブクエリ相性 △ 配列展開による IN句 が巨大化しやすい ◎ SQLとして組み込める

返り値の型

pluck

  • 配列を返す
irb(main):001:0> roles = User.pluck(:role)
   (1.2ms)  SELECT `users`.`role` FROM `users`
=> ["admin", "editor", "viewer"]

irb(main):002:0> roles.class
=> Array

irb(main):003:0> roles.first.class
=> String

select

  • 指定カラムのみを持つActiveRecord::Relationを返す
irb(main):001:0> users = User.select(:role)
  User Load (1.5ms)  SELECT `users`.`role` FROM `users`
=> 
[#<User:0x00007f8c8d7021c0 id: nil, role: "admin">, #<User:0x00007f8c8d702120 id: nil, role: "editor">]

irb(main):002:0> users.class
=> User::ActiveRecord_Relation

irb(main):003:0> users.first.class
=> User(id: integer, name: string, email: string, role: string, created_at: datetime, updated_at: datetime)

モデルメソッドの利用

app/models/user.rb
class User < ApplicationRecord
  def admin?
    role == "admin"
  end
end

pluck

  • 値(String)を返すためモデルメソッドは使えない
irb(main):001:0> role = User.pluck(:role).first
   (2.4ms)  SELECT `users`.`role` FROM `users`
=> "admin"

irb(main):002:0> role.admin?
(irb):2:in `<main>': undefined method `admin?' for "admin":String (NoMethodError)

select

  • モデルインスタンスを返すためモデルメソッドが使える
irb(main):003:0> user = User.select(:role).first
  User Load (1.5ms)  SELECT `users`.`role` FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: nil, role: "admin">

irb(main):004:0> user.admin?
=> true

サブクエリとの相性

pluck

  • SQLのIN句に配列展開される
  • 件数が多いとIN句が巨大化しパフォーマンス低下
irb(main):004:0> ids = User.where(role: "admin").pluck(:id)
irb(main):005:0> posts = Post.where(user_id: ids)
irb(main):006:0> posts.to_sql
=> "SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1, 2, 3, ..., 9999)"

select

  • サブクエリとして組み込まれる
  • DB側で処理され効率的
irb(main):001:0> subquery = User.select(:id).where(role: "admin")
irb(main):002:0> posts = Post.where(user_id: subquery)
irb(main):003:0> posts.to_sql
=> "SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (SELECT `users`.`id` FROM `users` WHERE `users`.`role` = 'admin')"

おわりに

  • 値だけ取得したい場合はpluckが軽量で高速
  • モデルの機能を使いたい場合やSQLに組み込みたい場合はselect
  • 目的に応じて使い分けることが重要

select 利用時の注意点

1. 取得していないカラムにはアクセスできない

irb(main):001:0> user = User.select(:role).first
  User Load (2.6ms)  SELECT `users`.`role` FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: nil, role: "admin">

irb(main):002:0> user.email
ActiveModel::MissingAttributeError: missing attribute: email

2. includesと併用する場合は外部キーを含める

irb(main):001:0> user = User.select(:id, :name).includes(:posts).first
  User Load (1.4ms)  SELECT `users`.`id`, `users`.`name` FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
  Post Load (1.0ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1)
=> #<User id: 1, name: "Hanako">

irb(main):002:0> user.posts
=> [#<Post id: 101, title: "Hello World", user_id: 1>, ...]
0
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
0
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?