by Markus Winand.

Getting an Execution Plan


A PostgreSQL execution plan is fetched by putting the explain command in front of an SQL statement. There is, however, one important limitation: SQL statements with bind parameters (e.g., $1, $2, etc.) cannot be explained this way—they need to be prepared first:

PREPARE stmt(int) AS SELECT $1

Note that PostgreSQL uses “$n” for bind parameters. Your database abstraction layer might hide this so you can use question marks as defined by the SQL standard.

The execution of the prepared statement can be explained:

EXPLAIN EXECUTE stmt(1)

Since PostgreSQL 9.2 the creation of the execution plan is postponed until execution and thus considers the actual values for the bind parameters. To obtain an execution plan that does not consider the actual values of the bind parameters, PostgreSQL 16 introduced the generic_plan option of explain.

Note

Statements without bind parameters can be explained directly:

EXPLAIN SELECT 1

In this case, the optimizer has always considered the actual values during query planning.

The explain plan output is as follows:

                QUERY PLAN                
------------------------------------------
 Result  (cost=0.00..0.01 rows=1 width=0)

The output has similar information as the Oracle execution plans shown throughout the book: the operation name (“Result”), the related cost, the row count estimate, and the expected row width.

Note that PostgreSQL shows two cost values. The first is the cost for the startup, the second is the total cost for the execution if all rows are retrieved. The Oracle database’s execution plan only shows the second value.

The PostgreSQL explain command has many options of which analyze, buffers and settings are the most helpful ones.

If you like this page, you might also like …

… to subscribe my mailing lists, get free stickers, buy my book or join a training.

Enabling the analyze option means that the execution plan is not only created, but also executed. That implies that the side effects of running the statement being explained will take place. Such as deleting rows when explain a delete statement. You can enclose explain in a transaction and perform a rollback afterwards if you don’t want such side effects to persist.

Warning

explain analyze executes the explained statement, even if the statement is an insert, update or delete.

Running the statement allows collecting run time benchmarks like the time and the actual number of rows produced by each operation. The option buffers also counts the number of database blocks being accessed.

Finally, the settings option also shows settings that differ from their default.

BEGIN
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
EXECUTE stmt(1)
                   QUERY PLAN
--------------------------------------------------
 Result  (cost=0.00..0.01 rows=1 width=4)
         (actual time=0.001..0.002 rows=1 loops=1)
 Settings: random_page_cost = '1.1'
 Planning Time: 0.032 ms
 Execution Time: 0.078 ms
ROLLBACK

Note that the plan is formatted for a better fit on the page. PostgreSQL prints the “actual” values on the same line as the estimated values.

The row count is the only value that is shown in both parts—in the estimated and in the actual figures. That allows you to quickly find erroneous cardinality estimates.

Last but not least, prepared statements must be closed again:

DEALLOCATE stmt
Previous pageNext page

You can’t learn everything in one day. Subscribe the newsletter via E-Mail, Bluesky or RSS to gradually catch up. Have a look at modern-⁠sql.com as well.

About the Author

Photo of Markus Winand

Markus Winand provides insights into SQL and shows how different systems support it at modern-sql.com. Previously he made use-the-index-luke.com, which is still actively maintained. Markus can be hired as trainer, speaker and consultant via winand.at.

Buy the Book

Cover of “SQL Performance Explained”: Squirrel running on grass

The essence of SQL tuning in 200 pages

Buy now!
(paperback and/or PDF)

Paperback also available at Amazon.com.

Hire Markus

Markus offers SQL training and consulting for developers working at companies of all sizes.
Learn more »

Connect with Markus Winand

Subscribe mailinglistsSubscribe the RSS feedMarkus Winand on LinkedInMarkus Winand on XINGMarkus Winand on TwitterMarkus Winand on Bluesky
Copyright 2010-2025 Markus Winand. All righs reserved.
Legal | Contact | NO WARRANTY | Trademarks | Privacy and GDPR