---
# System prepended metadata

title: How To read Explain Analyze?

---

# How To read Explain Analyze?

**User:**
I want to understand Explain Analyze result when I use PostgreSQL.
Could you generally tell me how to understand it?

***

**ChatGPT:**
When you use the <code>EXPLAIN</code> command in PostgreSQL, it analyzes the query plan for a specific SQL statement and returns information about how the query optimizer is going to execute the query. The output from <code>EXPLAIN</code> can be difficult to interpret, but it provides valuable information about the performance of the query and can help identify potential issues.
The output is divided into several sections, each of which provides information about a specific step in the query execution plan. The sections are typically labeled as "Seq Scan", "Index Scan", "Bitmap Heap Scan", etc. and they indicate the type of operation that the query optimizer has chosen to perform at that step.
The "cost" and "rows" values in the output represent the estimated cost and number of rows that the query optimizer expects to be returned from each step of the plan. These values can help identify which steps of the query are the most expensive in terms of performance.
The "actual time" and "actual rows" are the actual time and rows of the query execution.
It's also important to note that the query optimizer's choices are based on statistics about the table and indexes. So if the statistics are not accurate the optimizer could make the wrong choices, and the explain results will be not accurate.
It's recommended to use the <code>EXPLAIN ANALYZE</code> command to get the actual performance results of the query.
It's recommended to consult the official PostgreSQL documentation for more information on interpreting the output of the <code>EXPLAIN</code> command.

***

**User:**
What unit is for cost? US$?



***

**ChatGPT:**
The "cost" value in the output of the <code>EXPLAIN</code> command in PostgreSQL does not represent a monetary value in US dollars or any other currency. Instead, it is an arbitrary unit of measurement that the query optimizer uses to represent the relative cost of different operations in the query execution plan.
The cost is based on a cost model that takes into account the number of disk page fetches, CPU cycles and memory usage that each operation is expected to consume.
It's important to note that the cost values are relative and not absolute, meaning that they cannot be compared across different queries or different databases as they depend on the specific configuration of the database system and the data distribution.
It's also important to note that the actual time and rows may not match the estimated ones, but they are good indicators of the query performance.

***

**User:**
Can you share one example how to optimize a query by these indicators?



***

**ChatGPT:**
Sure, here's an example of how the <code>EXPLAIN</code> command and its output can help optimize a query:
Let's say we have a query that joins two large tables, "orders" and "customers", and we want to find all orders placed by customers in a specific state:
```vbnet
EXPLAIN ANALYZE
SELECT * 
FROM orders 
JOIN customers 
ON orders.customer_id = customers.id 
WHERE customers.state = 'CA';
```
The output of the <code>EXPLAIN ANALYZE</code> command might look something like this:
```sql
QUERY PLAN                                                  
----------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=100000.00..200000.00 rows=100000 width=858) (actual time=1000.00..2000.00 rows=1000 loops=1)
   Hash Cond: (orders.customer_id = customers.id)
   ->  Seq Scan on orders  (cost=0.00..100000.00 rows=1000000 width=426) (actual time=0.01..1000.00 rows=1000000 loops=1)
   ->  Hash  (cost=50000.00..50000.00 rows=10000 width=432) (actual time=0.01..50.00 rows=10000 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 512kB
         ->  Seq Scan on customers  (cost=0.00..50000.00 rows=10000 width=432) (actual time=0.01..25.00 rows=10000 loops=1)
               Filter: (state = 'CA'::bpchar)
               Rows Removed by Filter: 90000
```
From the output, we can see that the query is using a Hash Join operation and it's taking 2000.00 ms to complete. Also, we can see that the query optimizer did a full scan of both the "orders" and "customers" tables, which is not very efficient.
One way to optimize this query would be to add an index on the "state" column of the "customers" table, so that the query optimizer can find the relevant customers more quickly:
```java
CREATE INDEX customers_state_idx ON customers (state);
```
Then, if we run the same query again with the new index, the output of the <code>EXPLAIN ANALYZE</code> command might look something like this:
```sql
QUERY PLAN                                                  
----------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=1000.00..2000.00 rows=1000 width=858) (actual time=10.00..20.00 rows=1000 loops=1)
   Hash Cond: (orders.customer_id = customers.id)
   ->  Seq Scan on orders  (cost=0.00..100000.00 rows=1000000 width=426) (actual time=0.01..1000.00 rows=1000000 loops=1)
   ->  Index Scan using customers_state_idx on customers  (cost=0.00..1000.00 rows=1000 width=432) (actual time=0.01..10.00 rows=1000 loops=1)
         Index Cond: (state = 'CA'::bpchar)
```
Now, the query optimizer is using an index scan on the "customers" table, which is much faster

***

**User:**
What differences between Seq Scan and Index Scan?

***

**ChatGPT:**
A sequential scan (Seq Scan) and an index scan (Index Scan) are both ways for PostgreSQL to retrieve data from a table, but they work in different ways.
A sequential scan, as the name suggests, scans the entire table sequentially, row by row, to find the data that matches the query. It doesn't use any indexes to find the data and it's typically slower than using an index. This type of scan is used when there's no index that can be used to find the data or if the query optimizer determines that it would be more efficient to scan the entire table.
An index scan, on the other hand, uses an index to find the data that matches the query. An index is a separate data structure that stores a copy of the data in the table, but organized in a way that makes it faster to find specific rows. When an index scan is used, the query optimizer first looks up the relevant data in the index, and then uses the information from the index to find the actual data in the table. This is generally faster than a sequential scan, especially for large tables and complex queries.
It's also important to note that there are other types of scans like Bitmap Heap Scan, Bitmap Index Scan, and others, but the main idea is that the index scan is generally faster than a sequential scan. Also, the query optimizer will choose the best scan type depending on the query and the table statistics.

***

**User:**
How to choose the scan way? Like, in which situation we should choose Seq Scan, Index Scan, Bitmap Heap Scan, Bitmap Index Scan, and others?

***

**ChatGPT:**
The query optimizer in PostgreSQL automatically chooses the best way to retrieve data from a table based on the query and the statistics of the table. The optimizer will consider several factors when making this decision, such as the size of the table, the complexity of the query, the selectivity of the predicates (e.g. how selective are the conditions in the WHERE clause), and the existence and distribution of indexes on the table.
In general, the query optimizer will choose an index scan when an appropriate index is available that can be used to find the relevant data quickly. This is typically faster than a sequential scan, especially for large tables and complex queries.
If there's no index that can be used to find the data, or if the optimizer determines that it would be more efficient to scan the entire table, a sequential scan will be used.
Bitmap Heap Scan and Bitmap Index Scan are ways for the query optimizer to scan the table using bitmap index. A bitmap index is a data structure that represents the rows of a table using a bitmap. Each bit in the bitmap represents a row in the table and its value indicates whether the row satisfies a certain condition. Bitmap Heap Scan uses a bitmap index to scan the table, while Bitmap Index Scan uses the bitmap index directly. These types of scans are typically used when the query optimizer determines that it would be more efficient to use a bitmap index instead of a traditional index or a sequential scan.
It's also worth mentioning that there's another scan type called TID (Tuple Identifier) scan, it's a way to quickly locate individual rows of a table using the TID (Tuple Identifier) of the row.
In general, the query optimizer will take into account various factors when deciding which scan method to use, but it's important to note that the optimizer may not always choose the best scan method and sometimes it's necessary to review the query, indexes and statistics of the table to improve the performance.

***

**User:**
How to review the query?
It seems like the only way to optimize the query from developer is adding index. 
But, there are more ways to do. Could you share some samples for me?

***

**ChatGPT:**
There are several ways to review and optimize a query in PostgreSQL, in addition to adding indexes. Here are a few examples:
* <p><strong>Simplifying the query</strong>: Review the query and see if there's a way to simplify it without changing the meaning of the query. For example, removing unnecessary joins or conditions in the WHERE clause can greatly improve the performance of the query.</p>
* <p><strong>Optimizing the SELECT clause</strong>: Review the SELECT clause and see if there are any columns that are not needed in the query. If there are, remove them as they will be unnecessary computation for the query. Also, see if there are any columns that could be computed at the application level instead of at the database level.</p>

* <p><strong>Optimizing the WHERE clause</strong>: Review the WHERE clause and see if there are any predicates that are not selective enough. If so, try to make them more selective, for example by adding more conditions to the predicate. Also, see if there are any predicates that are not sargable, meaning they cannot use an index. If so, try to rewrite them to make them sargable.</p>

* <p><strong>Optimizing the JOIN clause</strong>: Review the JOIN clause and see if there are any joins that are not needed. If there are, remove them. Also, see if there are any joins that are causing performance issues. If so, try to reorder the joins or add more indexes to the tables that are being joined.</p>

* <p><strong>Using subquery</strong>: Review the query and see if a subquery could be used to improve the performance of the query. Subqueries are used to retrieve data from another table, and they can be used to avoid unnecessary joins or to improve the selectivity of predicates.</p>

* <p><strong>Using Materialized Views</strong>: Review the query and see if a materialized view could be used to improve the performance of the query. Materialized views are pre-aggregated or pre-joined data that can be used to improve query performance. They can also be useful when a query requires the same data from multiple tables.</p>

* <p><strong>Using Partitioning</strong>: Review the query and see if partitioning could be used to improve the performance of the query. Partitioning allows for a table to be divided into smaller, more manageable pieces, which can improve query performance.</p>

* <p><strong>Using EXPLAIN ANALYZE</strong>: Use the <code>EXPLAIN ANALYZE</code> command to analyze the query and get detailed information about how the query optimizer is executing the query. This can help identify any performance bottlenecks or areas that need improvement.</p>

It's important to note that the best way to optimize a query is dependent on the specific use case and the data distribution. It's recommended to test different options and compare the performance results to determine the best approach.


**User:**
When we write a subquery, is it possible to set an index for the result of this subquery?



***

**ChatGPT:**
Yes, it is possible to set an index for the result of a subquery in PostgreSQL. The result of a subquery can be treated as a virtual table, and you can create an index on this virtual table just as you would create an index on any other table.
There are two main ways to create an index on the result of a subquery:
<li><strong>Creating a temporary table</strong>: One way to create an index on the result of a subquery is to create a temporary table with the result of the subquery, and then create an index on the temporary table. For example:</li>
```sql
CREATE TEMP TABLE temp_table AS (SELECT * FROM subquery);
CREATE INDEX temp_table_idx ON temp_table (column_name);
```
<li><strong>Using a common table expression (CTE)</strong>: Another way to create an index on the result of a subquery is to use a common table expression (CTE) to define the subquery, and then create an index on the CTE. For example:</li>
```vbnet
WITH cte AS (SELECT * FROM subquery)
CREATE INDEX cte_idx ON cte (column_name);
```
It's important to note that the indexes created on temporary tables or CTEs will only be valid for the current session, and will be dropped when the session ends.
It's also worth mentioning that creating an index on a subquery will make the subquery more efficient, but it will also increase the memory usage and the time needed to create the index. It's recommended to carefully evaluate if the benefits of the index outweigh the costs.
It's important to check the selectivity of the column you want to create the index on, if it's not selective enough, it will not make a big difference creating an index on it.

***

**User:**
Could you give me an example of increasing the speed of query by adding the index to the subquery?

***

**ChatGPT:**
Sure, here's an example of how adding an index to a subquery can increase the speed of a query:
Let's say we have a query that retrieves the average salary of employees in a specific department:
```sql
SELECT AVG(salary) FROM employees WHERE department = 'IT';
```
The query above is using a sequential scan to find the relevant data in the "employees" table, which can be slow for large tables. To optimize this query, we can add an index on the "department" column of the "employees" table:
```java
CREATE INDEX employees_department_idx ON employees (department);
```
Now, when we run the same query again, the query optimizer will use the new index to find the relevant data in the "employees" table, which should be faster than a sequential scan.
Now, let's say we want to get the average salary of employees who have been in the company for more than 5 years, but the join between employees and the employment history table is slow.
```sql
SELECT AVG(e.salary) FROM employees e
JOIN employment_history eh ON e.id = eh.employee_id
WHERE eh.years_of_service > 5;
```
We can create an index on the "years_of_service" column of the "employment_history" table to speed up the query.
```java
CREATE INDEX employment_history_years_of_service_idx ON employment_history (years_of_service);
```
Now, when we run the same query again, the query optimizer will use the new index to find the relevant data in the "employment_history" table, which should be faster than a full scan on this table.
It's important to note that adding an index to a subquery may not always improve the performance of the query and it depends on the specific use case and the data distribution, but it's worth testing it as it could make a big difference on the performance.

***

**User:**
How to destroy this table of subquery? Or, it will destroy by itself?



***

**ChatGPT:**
When you create a temporary table or a common table expression (CTE) with the result of a subquery, the table or CTE is only valid for the current session, and it will be automatically destroyed when the session ends. This means that you don't have to explicitly destroy the table or CTE, it will be destroyed automatically when the session ends.
For temporary tables you can use the command <code>DROP TABLE</code> to remove it manually before the end of the session:
```sql
DROP TABLE temp_table;
```
For CTEs, you can use the <code>WITH</code> clause again but with the keyword <code>DROP</code> before the name of the CTE, this will remove the CTE before the end of the session:
```sql
WITH DROP cte
```
It's also worth mentioning that any indexes that you create on a temporary table or a CTE will also be automatically destroyed when the table or CTE is destroyed.
It's important to keep in mind that these temporary tables and CTEs are only visible to the current session, so you don't have to worry about them being visible to other sessions or interfering with other queries.

***

**User:**
In the case above, adding the index could be good in any context.
I wonder if there is a context that is not good to add index when we only need to read the data.
Specifically, if we do not need to write data in a database, is indexing all columns a good way?

***

**ChatGPT:**
In general, adding indexes can improve the performance of read-only queries by allowing the query optimizer to find the relevant data more quickly. However, there are certain contexts where adding indexes may not be the best option, specifically when:
<li><p><strong>The table is small</strong>: If the table is small and the query is simple, adding an index may not provide much of a performance improvement, and in some cases it could even slow down the query.</p></li><li><p><strong>The table is not frequently queried</strong>: If the table is not frequently queried, the time and resources required to maintain the index may outweigh the benefits of the index.</p></li><li><p><strong>The table is updated frequently</strong>: If the table is updated frequently, the time and resources required to maintain the index may outweigh the benefits of the index.</p></li><li><p><strong>The column is not selective enough</strong>: If the column you want to create an index on is not selective enough, the index may not be useful and will not improve the performance.</p></li><li><p><strong>The query is using full-text search</strong>: If the query is using full-text search, the index may not be useful and will not improve the performance.</p></li>
In general, it's not a good idea to add indexes to all columns in a table, especially if the table is frequently updated, or if the column is not selective enough. It's recommended to evaluate the specific use case and the data distribution before adding an index.
It's also important to

***

**User:**
What does number 4 and number 5 specifically mean?

***

**ChatGPT:**
Number 4 refers to the selectivity of the column, selectivity is a measure of how unique the values in a column are. Columns with high selectivity have unique values, while columns with low selectivity have many duplicated values. When creating an index, it's important to evaluate the selectivity of the column because if the column is not selective enough, meaning that the values in the column are not unique, the index may not be useful and will not improve the performance.
Number 5 refers to full-text search, it's a type of search that looks for a specific word or phrase within a larger body of text. Full-text search is different from regular search because it looks for keywords within the text, rather than matching a specific value. PostgreSQL has built-in support for full-text search, but it requires a specific type of index called a full-text search index. It's different from the regular index and it's not recommended to use regular indexes when the query is using full-text search.
In general, it's important to evaluate the specific use case and the data distribution before adding an index, and it's not always a good idea to add indexes to all columns in a table, especially if the table is frequently updated, or if the column is not selective enough or it's used for full-text search.

***

**User:**
Could you give me an example of full-text search?

***

**ChatGPT:**
Sure, here's an example of how to use full-text search in PostgreSQL:
Let's say we have a table called "articles" with a column called "content" that contains the text of the articles. We want to search for articles that contain the word "PostgreSQL".
First, we need to create a full-text search index on the "content" column:
```sql
CREATE INDEX articles_content_ftx ON articles USING gin(to_tsvector('english', content));
```
This creates a full-text search index using the <code>gin</code> (Generalized Inverted Index) operator, and it specifies that the index should be created on the "content" column, using the English language.
Then we can use the <code>to_tsquery()</code> function to convert the search string to a full-text search query, and use the <code>@@</code> operator to check for a match between the query and the index:
```sql
SELECT * FROM articles WHERE to_tsvector('english', content) @@ to_tsquery('english', 'PostgreSQL');
```
This query will return all the articles that contain the word "PostgreSQL" in the "content" column.
It's worth mentioning that full-text search has many features like ranking, stemming, stopwords and more, also there are other operators like <code>tsquery</code> and <code>tsvector</code> that can be used to perform more advanced search.
Also, it's important to note that the use of full-text search index can improve the performance of the search, but it also has a cost of maintaining the index and the search process, so it's recommended to evaluate the specific use case and the data distribution before using it.

***

**User:**
What is a cost of maintaining the index concretely?


***

**ChatGPT:**
The cost of maintaining an index can be broken down into two main categories: space cost and time cost.
<li><p><strong>Space cost</strong>: Maintaining an index requires additional storage space to store the index data. The size of the index will depend on the number of rows in the table, the size of the indexed columns, and the type of index. In general, indexes take up more space than the table itself. So, if you have a table with a lot of data, the index will take up a significant amount of space.</p></li><li><p><strong>Time cost</strong>: Maintaining an index also requires additional time to update the index when the table is modified. For example, when inserting, updating or deleting a row in the table, the corresponding row in the index must also be updated. This process can take some time and can slow down the performance of the write operations. Additionally, if the table is frequently updated, the cost of maintaining the index can outweigh the benefits of the index.</p></li>
It's also worth mentioning that creating and maintaining indexes can consume additional resources such as CPU and memory.
It's important to keep in mind that the cost of maintaining an index will depend on the specific use case, the data distribution, the size of the table and the update frequency. It's recommended to evaluate these factors before adding an index and monitor the performance of the database to ensure that the benefits of the index outweigh the costs.

***

**User:**
So, I want to easily summarize a few points, and I hope you can confirm my learning points.

1. If the selectivity of the column, a measure of how unique the values in a column are, is low, I should not add an index because the benefit is low.

2. However, the column for full-text search is also high selectivity of the column, it is also not a good way to add a regular index.

***

**ChatGPT:**
Yes, those are correct.
<li><p>If the selectivity of the column is low, it means that the values in the column are not unique and adding an index may not provide much of a performance improvement. In this case, the index may not be useful and the benefit is low.</p></li><li><p>For full-text search, it's also important to note that regular indexes are not the best option, it's different from regular search because it looks for keywords within the text, rather than matching a specific value. PostgreSQL has built-in support for full-text search, but it requires a specific type of index called a full-text search index. It's recommended to use the full-text search index when performing full-text search.</p></li>
It's important to evaluate the specific use case and the data distribution before adding an index and monitor the performance of the database to ensure that the benefits of the index outweigh the costs.


***

**User:**

If I have those two points, I do not need to use explain analyze to optimize the query?


***

**ChatGPT:**

While it's true that understanding the selectivity of a column and knowing when to use full-text search index can help you optimize your queries, using the EXPLAIN ANALYZE command is still a valuable tool for identifying performance bottlenecks and understanding how the query optimizer is executing the query.

EXPLAIN ANALYZE provides detailed information about how the query optimizer is executing the query, including the execution plan, the number of rows that were processed, and the time it took to execute the query. This information can help you identify areas that need improvement and understand the impact of different optimizations.

For example, if you have a query that is performing a sequential scan, you may suspect that adding an index would help improve performance. But, by using EXPLAIN ANALYZE to see the execution plan, you could confirm that the query is already using an index, and the performance issue is caused by a different problem.

It's recommended to use the EXPLAIN ANALYZE command as a tool to help identify and troubleshoot performance issues, and it's also useful to test different optimization options and compare the performance results.





###### _Exported by **黃俊豪** on 1/16/2023, 7:20:38 PM_