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"