.pick: the single-row shortcut of .pluck

Free preview 3 min to read

Where this picks up

The lesson "SELECT & FROM in Rails", in Raw SQL: select & from, covered the basics. .select(:name, :email) returns model objects with only the chosen columns filled in, and an unselected column reads back nil, which looks the same as a database NULL. .pluck(:name) skips building models and returns a plain array. A table in that lesson showed when to use .pluck, .select or .exists?. ActiveRecord querying: order/limit used .pluck constantly to print results without noise. This course goes further: .pick, the query-count difference between .pluck and .map(&:attr), .pluck across a join, distinct.pluck, and a trap specific to this app's Translatable concern.

The question .pick answers: one value, not a list

"What's the slug of the lowest-position course?" needs one string, not an array. .pluck always returns an array, even when only one row matters:

Catalog::Course.order(:position).pluck(:slug).first
# => "computer-architecture"

That works, but it fetches the slug of every matching course and then throws away all but the first in Ruby. .pick does the same job without the waste:

Catalog::Course.order(:position).pick(:slug)
# => "computer-architecture"

.pick runs LIMIT 1, .pluck.first doesn't

Here is the SQL each one sends, against this app's Catalog::Course table:

Course.order(:position).pick(:slug)
SELECT "courses"."slug" FROM "courses" ORDER BY "courses"."position" ASC LIMIT 1

Course.order(:position).pluck(:slug).first
SELECT "courses"."slug" FROM "courses" ORDER BY "courses"."position" ASC

.pick puts LIMIT 1 in the SQL, so the database stops after the first row. .pluck(:slug).first has no LIMIT. It pulls the slug of every course (a few hundred in this catalog) across the wire, builds an array that size in Ruby, and only then calls .first to keep one. On a small table you won't notice; on a large one, .pick is the one that scales.

.pick is built on .pluck

The Rails log labels a .pick call Course Pluck, not Course Pick. That isn't a display bug. Internally, .pick is .limit(1).pluck(*columns).first, the same mechanism this lesson just described by hand. That explains the label, and it means .pick inherits what .pluck does: no model objects, raw values back, and several columns supported the same way.

Several columns work too

Catalog::Course.order(:position).pick(:slug, :level)
# => ["computer-architecture", "intern"]

This is .pluck's multi-column shape (an array, not a hash) for the one row .pick stops at. It's the same array-of-values-per-row rule that "SELECT & FROM in Rails" showed for .pluck(:name, :email), cut down to a single row.

Key takeaways

  • "SELECT & FROM in Rails" covered the nil trap of .select and the plain-array return of .pluck. This course goes on to .pick, query counts, joins and a Translatable trap.
  • .pick(:column) returns a single value for one row, not an array. .pluck(:column).first imitates that by discarding everything after the first element.
  • .pick adds LIMIT 1 to the SQL, so the database stops after one row. .pluck(:column).first has no LIMIT and fetches every matching row's column before Ruby keeps the first.
  • .pick is .limit(1).pluck(*columns).first inside, which is why the Rails log labels it "Pluck". It keeps the behavior of .pluck: no model objects, and an array for several columns.
Lesson 1 of 7 · free

Keep going with lesson 2

.pluck vs .map(&:attr): what costs a query

.pluck narrows the SELECT on unloaded relations, reads memory on loaded ones, and always queries for SQL expressions.

A free account also gives you the quizzes, your progress and notes, and the discussion.

Type to start searching...