pluckメソッドについて

pluckとは?

1つのモデルで使用されているテーブルからカラム (1つでも複数でも可) を取得するクエリを送信するのに使用できます。引数としてカラム名のリストを与えると、指定したカラムの値の配列を、対応するデータ型で返します。(Railsガイドより)

基本形は

モデル名.pluck(:カラム名)

戻り値について

引数なしの場合

SQL SELECT "users".*から全てのデータを取得していることが分かります。
値は2次元配列(配列の中に配列)として返します。

User.pluck
SELECT "users".* FROM "users"    # <= usersテーブルから全てのデータを取得する

=> [
[1, "田中", "健二"],
[2, "松尾, ""],
[3, "池田", ""]
]

引数が一つの場合

引数で指定したカラムのみを取得します。また、値は配列として返します。

User.pluck(:last_name)
 SELECT "users"."last_name" FROM "users"  #<= usersテーブルからlast_nameカラムのみ取得している

=> ["田中", "松尾", "池田"]

引数が複数の場合

値は2次元配列として返します。

User.pluck(:last_name, :first_name)
SELECT "users"."last_name", "users"."first_name" FROM "users"

=> [["田中", "健二"], ["松尾", ""], ["池田", ""]]

pluckの後に他のクエリメソッドをチェーンできない!!

pluckの戻り値は配列なので、その後にクエリメソッドをチェーンすることはできません。

 User.pluck(:last_name).limit(1)
   (0.2ms)  SELECT "users"."last_name" FROM "users"
# NoMethodError: undefined method `limit' for #<Array:0x00007f8def865770>

# pluckの前に配置することでエラーを回避できます。

User.limit(1).pluck(:last_name)
   (0.2ms)  SELECT  "users"."last_name" FROM "users" LIMIT ?  [["LIMIT", 1]]
=> ["田中"]

mapメソッドとの違い

先程のpluckを使用してlast_nameカラムを取得する場合は以下のようになります。

User.pluck(:last_name)
SELECT "users"."last_name" FROM "users"
=> ["田中", "松尾", "池田"]

これと等価の値をmapを使用して取得する場合には以下のようになります。

User.all.map(&:last_name)
SELECT "users".* FROM "users"
=> ["田中", "松尾", "池田"]

2つの違いはSQL文を見ると分かります。
pluckはSELECT "users"."last_name"とあるようにDBから取得する際にデータを絞り込んでいるのが分かります。
それに対して、mapはSELECT "users".* FROM "users"とあるようにDBから全てのデータを取得してから絞り込んでいるのが分かります。

このことから特定のカラムのみを扱いたい場合はpluckを使用する方がメモリの消費を抑えることができます!!

mapを使用した方がいい場合

インスタンス化されたオブジェクトに対して実行する場合はmapの方がパフォーマンスはいいです。 以下のようにループ文の中でそれぞれを実行した場合

users = User.all

Benchmark.bm 10 do |r|
  r.report "pluck" do  
    2000.times { users.pluck(:id) }    
  end  
  r.report "map" do   
    2000.times { users.map(&:id) }    
  end  
end  
               user     system      total        real
pluck        0.148876   0.000580   0.149456 (  0.149820)
map          0.059511   0.000156   0.059667 (  0.059783)

これはpluckは毎回SQL(2000回)を発行しているのに対して、mapは一度しかSQLを発行していないからです。