PostgreSQL Query Optimization Performance Tuning with EXPLAIN ANALYZE

In today’s remotely operated workplace environment, the challenges of query optimization and performance in databases can be magnified. With face-to-face collaboration reduced, tools like PostgreSQL’s EXPLAIN command are essential for identifying and resolving performance bottlenecks in database queries. EXPLAIN provides a detailed query plan that lays out the steps PostgreSQL takes to execute a query, helping you understand whether and why an index is being used or ignored.

A common pitfall is to overlook how a query is structured, affecting how the database interprets and utilises indexes. A case in point involves a situation where a query’s performance was hindered by the use of a function that prevented the use of an existing index. A simple adjustment to the query enabled the database to utilise the index effectively, illustrating the importance of understanding the relationship between query structure and index usage. Leveraging EXPLAIN ANALYZE can significantly enhance database query performance, transforming the once daunting task into a manageable one.

Key Takeaways

  • EXPLAIN is a crucial command for diagnosing and improving database query performance.
  • Query structure influences whether an index is used, impacting query efficiency.
  • Adjusting query syntax can enable proper index utilization and improve performance.

Understanding Explain

Explain is a command that you can attach before any SQL query to gain insight into how the database’s query planner anticipates executing your command. It reveals the approach and method the query will utilise, including:

  • Join strategies: How tables are connected
  • Data retrieval methods: The way information is pulled from the database
  • Estimates: Predicted number of rows the query will engage

When coupled with Analyse, Explain goes further by providing actual execution times and detailing operations that the system couldn’t process in memory, such as sorts and merges. This diagnostic data is critical for pinpointing where a query’s performance might falter and for making informed decisions about optimising it.

Understanding the Expense-Oriented Method

When you’re interacting with a database like PostgreSQL, it’s important to recognise that the task of retrieving data efficiently is guided by how the system evaluates the cost of various operations. These operations can range from scanning an entire table to combining the data from different tables. The efficiency of these operations depends largely on their estimated time consumption.

PostgreSQL uses a system where each potential execution task is assigned a cost. These estimations are found in the postgresql.conf file. Costs you’ll find there include settings with suffixes like _cost or prefixes such as enable_.

Consider this scenario: When you issue a query, PostgreSQL’s query planner gets to work by summing up the costs for all feasible execution paths. It then chooses the path with the optimal balance between speed and resource use.

For a more concrete understanding, let’s look at an example:

Nested Loop  (cost=0.00..4141.00 rows=99999 width=461)
  - Join Filter: ( =
  - > Seq Scan on pgbench_branches b  (cost=0.00..1.01 rows=1 width=364)
  - > Seq Scan on pgbench_accounts a  (cost=0.00..2890.00 rows=99999 width=97)

In the aforementioned plan, the sequential scan on the pgbench_accounts has a cost of 2890. This value isn’t arbitrary; it’s calculated through a formula balancing multiple factors.

Here’s how the cost is generally broken down:

  • Block Access Cost: Number of blocks multiplied by the cost to scan a single page (seq_page_cost).
  • Tuple Processing Cost: Number of records processed multiplied by the cost per tuple operation (cpu_tuple_cost).
  • Filter Application Cost: Number of operations for applying filters multiplied by the respective cost (cpu_filter_cost).

By utilising these parameters, you calculate the cost like so:

Cost = (Number of Blocks × seq_page_cost) + (Number of Records × cpu_tuple_cost) + (Number of Records × cpu_filter_cost)

With the right configuration and understanding of these parameters, you are better equipped to optimise your query performance and ensure more efficient data retrieval.

Insight on Statistical Data for Query Costs

In assessing query costs, your query planner utilises statistical data. This data isn’t something you could easily interpret by a glance at pg_statistic. For a clearer picture regarding your tables and rows, pg_stats offers more accessible information. Remember, inaccurate statistics can cause the query planner to select a plan that’s not the most efficient, potentially degrading your query performance.

Your primary focus should be on maintaining the accuracy of these statistics; they are crucial for optimising performance. They aren’t updated instantaneously and depend on the frequency of PostgreSQL’s internal processes. Be sure to carry out regular database maintenance. This includes routine tasks such as:

  • Vacuuming: To reclaim space from deleted rows and avoid table bloat.
  • Analysing: To update statistics and improve the query planner’s assessments.

Regularly neglected statistics might lead to issues like this:

Before Analysis

Seq Scan on pgbench_history  (cost=0.00..2346.00 rows=35360 width=50)
Filter: (aid < 100)

This sequential scan could be the result of stale statistics, leading to a less efficient path chosen by the query planner. After implementing statistical updates via an ANALYZE command, the query planner’s choice might improve significantly:

After Analysis

Index Scan using foo on pgbench_history  (cost=0.42..579.09 rows=153 width=50)
Index Cond: (aid < 100)

Here, an index scan is selected, which generally offers enhanced performance over a sequential scan for this type of query. Regularly scheduling these maintenance tasks will ensure the statistical data guiding your query plans remain as accurate as possible, aiding in the selection of the most efficient execution paths for your queries.


When appending EXPLAIN ANALYZE to a SQL query, it gives you a detailed report on how the query is executed. This tool will actually run the query and provide insight into each step of the execution plan.

Here’s what EXPLAIN ANALYZE can reveal:

  • Actual Timing: How long each part of the query takes to execute.
  • Row Operations: The number of rows processed and details about join operations.
  • Planning vs. Execution Time: Separate metrics for the time it takes to plan the query and the time it takes to execute it.

Consider an example for clarity:


This might return an output detailing the sequential scans, join types and filters applied, alongside metrics such as:

  • Cost Estimates: These are the planner’s best guesses before execution.
  • Actual Time: The real time taken to carry out the operation.
  • Rows Removed by Filter: Data that was excluded due to query conditions.

Incorporating BUFFERS, like so:


…adds another layer of detail about cache usage:

  • Buffers: Indicates shared cache hits and reads, which tells you how effectively the database caches are being utilised.

By integrating EXPLAIN ANALYZE into your performance tuning processes, you can gain precise diagnostic information, from the cost of individual operations to execution duration, and better understand the efficiency of data retrieval in your PostgreSQL databases.

A Quick Review of Scan Types and Joins

When tackling queries in PostgreSQL, your approach to scanning and joining tables can significantly influence performance. Below is an overview to better understand these processes.

Scan Types

  • Sequential Scan: Efficient for quickly sifting through small datasets straight from the disk. This full-table read avoids the need for indexes when the size justifies it.

  • Index Scan: Useful when a query targets fewer rows within large datasets, despite the potential slowdown from random disk accesses, especially on traditional spinning disks.

  • Index Only Scan: Operates directly within the index to fetch required data, bypassing table lookups entirely and offering a performance advantage when all required data resides in the index.

  • Bitmap Heap Scan: Constructs a bitmap from index information to isolate relevant pages in the table before retrieving the desired data, optimising the process of fetching non-contiguous rows.

Join Types

  • Nested Loops: Pairs each row from one table with the corresponding rows in another, best suited for smaller datasets as it’s quick to initiate.

  • Merge Join: Think of this as merging two sorted arrays. It thrives on sorted data and is beneficial for larger datasets, albeit at the cost of potentially requiring an additional sort.

  • Hash Join: Builds a hash table from one dataset, allowing efficient searches for matching entries in the second dataset. While costly at startup, this type allows for speedy execution of join operations with equality conditions.

Your query performance hinges upon the PostgreSQL planner having robust statistics to make informed decisions. Mistuning or not maintaining the system can lead to suboptimal scan or join choices, resulting from:

  • Erroneous statistics skewing decision-making,
  • Lackadaisical maintenance activities,
  • Blemished indexes,
  • Misalignment between index definitions and queries,
  • Insufficient memory allocations hindering in-memory operations,
  • Counterproductive join orders or configurations when constructing queries.

Leveraging the EXPLAIN command is indispensable for discerning and addressing these issues, ensuring you spend less time troubleshooting and more time capitalising on PostgreSQL’s capabilities.

Join Postgres Pulse Live!

Experience a real-time discussion on Postgres challenges! Postgres Pulse Live Sessions offer a unique platform where you can enhance your understanding of EXPLAIN and query planning. Get involved in the upcoming event:

  • Date: Monday, 4th May
  • Focus: EXPLAIN usage, query questions & solutions
  • Participate:
    • Send queries to: [email protected]
    • Use Twitter: include a hashtag
    • Ask live: Engage during the session

How to join: A link to the live session is readily available through our blog and YouTube series. Whether you’re looking to solve complex query issues or simply wish to broaden your Postgres knowledge, this session is designed to cater to your needs. Don’t miss out on the chance to submit your questions and interact with experts.

Frequently Asked Questions

Interpreting EXPLAIN ANALYZE Output for Query Performance Enhancement in PostgreSQL

Upon executing the EXPLAIN ANALYZE, you’ll be presented with detailed insights into how PostgreSQL executed your query. This involves a breakdown of the query plan, showing sequential scan times, index usage, and execution costs for each operation. By closely examining the sequence in which operations are executed and the time spent on each, you can pinpoint inefficiencies and make adjustments to your query structures or indexes accordingly.

Best Practices for SELECT Statement Efficiency

To optimise your SELECT queries:

  • Use specific column names instead of * to reduce data retrieval load.
  • Implement proper indexing based on query patterns.
  • Leverage WHERE clauses to filter data and reduce result set size early.
  • Avoid unnecessary joins when possible to decrease complexity.

Differences Between EXPLAIN and EXPLAIN ANALYZE


  • Provides an estimated query plan without executing the query.
  • Useful for quick plan inspection.


  • Executes the query and provides actual runtime statistics.
  • More insightful for performance tuning but with added execution overhead.

Methodologies for Diagnosing Slow Queries

If you encounter slow queries:

  • Utilise EXPLAIN ANALYZE to observe plan execution details.
  • Check for sequential scans of large tables that can be optimised with indexes.
  • Look for queries with high total cost or long execution times to target for optimisation.

The Role of ANALYZE in SQL Query Tuning

Running ANALYZE updates the statistics used by the PostgreSQL query planner. Accurate statistics enable the planner to make better decisions about the most efficient way to execute a query, thus influencing query performance positively.

Tools for Visualisation of EXPLAIN ANALYZE in PostgreSQL

Several tools can assist you by presenting the EXPLAIN ANALYZE output in a more accessible format:

  • pgMustard: Offers visualisations and recommendations.
  • PEV – PostgreSQL Explain Visualizer: Generates interactive visual output.
  • Depesz’s Explain Analyze: Creates a colour-coded, hierarchical view of plans.

Leave a Comment