An ActiveRecord query by default uses the SELECT * operator to select all the fields from the result set. The ActiveRecord::QueryMethods#select method allows you to select a subset of fields from the result set.

Before Rails 7.1

ActiveRecord::QueryMethods#select and ActiveRecord::QueryMethods#reselect only accept strings, symbols, or raw SQL to define columns and aliases to select.

When querying a single model, we can provide column names as an array of strings or symbols, or we can provide raw SQL.

> Model.select(:field, :other_field)
> # OR
> Model.select('field', 'other_field')
> # OR
> Model.select('field, other_field')

But when you need to select the columns from an associated model, we only have the option to provide raw SQL.

> Model.joins(:sub_models).select('models.field AS models_field_alias, sub_models.field AS sub_models_field_alias')

Rails 7.1 onwards

Rails 7.1 adds the ability to pass a hash of columns and aliases to be selected with the ActiveRecord::QueryMethods#select and ActiveRecord::QueryMethods#reselect methods.

Model.select(:field, :other_field)
Model.select(field: :field_alias)
Model.select(table_name: [:field, :other_field])
Model.select(table_name: { field: :field_alias, other_field: :other_field_alias })

Examples

# Table name: customers
#
#  id               :integer
#  name             :string
#  email            :string
#  shipping_address :text
#  created_at       :datetime
#  updated_at       :datetime

class Customer < ApplicationRecord
  has_many :orders
end

# Table name: orders
#
#  id             :integer
#  date           :date
#  total          :decimal
#  customer_id    :integer
#  created_at     :datetime
#  updated_at     :datetime

class Order < ApplicationRecord
  belongs_to :customer
end
  • Without aliases

    Loading development environment (Rails 7.1.0.alpha)
    3.1.0 :001 > Customer.joins(:orders).select("customers.id, customers.name, orders.date, orders.total")
      Customer Load (0.2ms)  SELECT customers.id, customers.name, orders.date, orders.total FROM "customers" INNER JOIN "orders" ON "orders"."customer_id" = "customers"."id" /* loading for pp */ LIMIT ?  [["LIMIT", 11]]
    

    now written as ↓

    Loading development environment (Rails 7.1.0.alpha)
    3.1.0 :001 > Customer.joins(:orders).select(customers: [:id, :name], orders: [:date, :total])
      Customer Load (0.2ms)  SELECT "customers"."id", "customers"."name", "orders"."date", "orders"."total" FROM "customers" INNER JOIN "orders" ON "orders"."customer_id" = "customers"."id" /* loading for pp */ LIMIT ?  [["LIMIT", 11]]
    
  • With aliases

    Loading development environment (Rails 7.1.0.alpha)
    3.1.0 :001 > Customer.joins(:orders).select("customers.id, customers.name AS customer_name, orders.date AS order_date, orders.total AS order_total")
      Customer Load (0.2ms)  SELECT customers.id, customers.name AS customer_name, orders.date AS order_date, orders.total AS order_total FROM "customers" INNER JOIN "orders" ON "orders"."customer_id" = "customers"."id"
    

    now written as ↓

    Loading development environment (Rails 7.1.0.alpha)
    3.1.0 :001 > Customer.joins(:orders).select(customers: { id: :id, name: :customer_name }, orders: { date: :order_date, total: :order_total })
    Customer Load (2.0ms)  SELECT "customers"."id" AS id, "customers"."name" AS customer_name, "orders"."date" AS order_date, "orders"."total" AS order_total FROM "customers" INNER JOIN "orders" ON "orders"."customer_id" = "customers"."id"
    

The same applies for ActiveRecord::QueryMethods#reselect.

Note: If you want to use SQL functions in ActiveRecord::QueryMethods#select you still need to pass raw SQL. i.e.

Loading development environment (Rails 7.1.0.alpha)
3.1.0 :028 > Customer.joins(:orders).group('orders.customer_id').select("customers.id, COUNT(orders.id) AS order_count")
  Customer Load (1.2ms)  SELECT customers.id, COUNT(orders.id) AS order_count FROM "customers" INNER JOIN "orders" ON "orders"."customer_id" = "customers"."id" GROUP BY "orders"."customer_id"

References