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. sumThe 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.inspectLuckily,
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.