Rails 8 adds explain support to ActiveRecord::Relation
The explain
method in Rails' ActiveRecord is a powerful tool
for understanding how your database executes your queries.
It allows you to peek under the hood
and
see the exact SQL query that will be sent to the database
and
the execution plan the database has chosen to retrieve the data.
It provides insight into the sequence of operations,
indexes used,
and
estimated costs involved in executing a particular query.
The explain
method was first introduced in Rails 3.2.0.
Rails 7.1 allows passing :verbose
and
:analyze
options to
the ActiveRecord explain
method.
Please refer to our previous
blog post
to learn more about this feature.
While the explain
method worked well,
providing insights into the database queries,
it failed for queries that returned an ActiveRecord::Relation
.
Before Rails 8.0
Let's say you have a Rails application with the
Post
and
Comment
model.
The post model has a boolean column published
.
You can use the explain
method as shown to understand the
execution plan of the query.
> Post.where(published: true).explain
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."published" = $1 [["published", true]]
=>
EXPLAIN SELECT "posts".* FROM "posts" WHERE "posts"."published" = $1 [["published", true]]
QUERY PLAN
----------------------------------------------------------
Seq Scan on posts (cost=0.00..15.40 rows=270 width=121)
Filter: published
(2 rows)
The problem arrives when you try to use it on ActiveRecord methods
like count
,
last
,
or
pluck
.
> Post.where(published: true).count.explain
Post Count (0.5ms) SELECT COUNT(*) FROM "posts" WHERE "posts"."published" = $1 [["published", true]]
(irb):3:in `<main>': undefined method `explain' for 2:Integer (NoMethodError)
Post.where(published: true).count.explain
^^^^^^^^
> Post.where(published: true).pluck(:id).explain
Post Pluck (0.2ms) SELECT "posts"."id" FROM "posts" WHERE "posts"."published" = $1 [["published", true]]
(irb):5:in `<main>': undefined method `explain' for [1, 2]:Array (NoMethodError)
Post.where(published: true).pluck(:id).explain
^^^^^^^^
While the explain
method is a valuable tool
for understanding query performance in Rails' ActiveRecord,
it currently has limitations with methods like
last
,
pluck
,
and
count
that return single values
or
arrays instead of full relations.
This lack of relational context makes it harder to analyze
the query's execution plan
and
identify potential optimizations.
Rails 8.0
Rails 8.0 adds explain support to
ActiveRecord::Relation.
This change allows the developer to run the explain
function on the below methods.
1. average
2. count
3. first
4. last
5. maximum
6. minimum
7. pluck
8. sum
The post queries that failed before Rails 8.0 will run safely without raising any errors.
> Post.where(published: true).count.explain
EXPLAIN SELECT COUNT(*) FROM "posts" WHERE "posts"."published" = $1 [["published", true]]
> Post.where(published: true).pluck(:id)
EXPLAIN SELECT "posts"."id" FROM "posts" WHERE "posts"."published" = $1 [["published", true]]
Note:
This change breaks the existing functionality
of the explain
method.
You need to use inspect
after the explain
method.
> Post.all.explain.inspect
Luckily,
this limitation poses no issue in interactive environments like IRB,
where inspect
automatically provides valuable insights.
To know more about this feature, please refer to this PR.