This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references.
HTML source: No matching course-page link was captured in the source inventory.
Archive:QPT-handouts-16-en-20251018.zipPDF pages: 19
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
In no event shall Postgres Professional company be liable for any damages or loss, including loss of profits, that arise from direct or indirect, special or incidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
Functional dependency
Most frequent value combinations
Count of unique value combinations
Expression Statistics
Page 3: Advanced Statistics
Contains
Multivariate statistics (across multiple columns)
Expression Statistics
A database objectDatabase object created manually
resides in pg_statistic_ext and pg_statistic_ext_data
the pg_stats_ext and pg_stats_ext_exprs views
Once created, the statistics are automatically collected.
Speaker notes
The base statistics automatically collected may not be sufficient for accurate estimates of cardinality and selectivity.
PostgreSQL allows the database administrator to manually determine which additional, extended statistics are required. You can collect statistics that cover multiple columns (multivariate statistics) or statistics for arbitrary expressions.
Keep in mind that base statistics are automatically collected for tables and their columns, but not for indexes—except for expression indexes. Therefore, an index built on multiple columns does not automatically result in the generation of multivariate statistics.
Extended statistics can be created using the CREATE STATISTICS command. Once the object is created, the corresponding statistics are automatically collected in the background or via the ANALYZE command.
The collected information is stored in the tables pg_statistic_ext and pg_statistic_ext_data; the statistics accessible to users are displayed in the views pg_stats_ext and pg_stats_ext_exprs.
The value of one column defines the value of another column.
Statistics improves the estimation of condition selectivity.
Speaker notes
There are several types of multivariate statistics (i.e., statistics for multiple table columns) that can be specified when creating an extended statistics object.
Functional dependency between columns shows how much the data in one column is determined by the value of another column.
In the example on the slide, the postal code clearly defines the city, so the selectivity of the condition city = ‘Samara’ and index = ‘443000’ is determined by the selectivity of the index = ‘443000’ predicate. Such predicates, whose selectivity cannot be calculated independently of each other, are called correlated.
QUERY PLAN
------------------------------------------------------------------------------------------
-----------------
Bitmap Heap Scan on flights (cost=12.13..1264.75 rows=26 width=63)
Recheck Cond: (flight_no = 'PG0007'::bpchar)
Filter: (departure_airport = 'VKO'::bpchar)
-> Bitmap Index Scan on flights_flight_no_scheduled_departure_key (cost=0.00..12.12
rows=494 width=0)
Index Cond: (flight_no = 'PG0007'::bpchar)
(5 rows)
The issue arises because the planner assumes predicate independence and calculates the final selectivity as the product of individual predicate selectivities. For example, in the provided plan, the estimate in the Bitmap Index Scan node (flight_no condition) is accurate, but after filtering in the Bitmap Heap Scan node (departure_airport condition), it’s significantly underestimated.
However, we know that the flight number uniquely identifies the departure airport: essentially, the second condition is redundant (assuming the airport is correctly specified).
This can be explained to the planner using functional dependency statistics:
The Statistics objects section lists the names, columns, and non-standard target values of the statistics objects.
Page 7: Most Frequent Combinations
{#source-figure-007 caption=“Source slide page 7: Most Frequent Combinations”}
Most Common Value Combinations (MCV)
Similar to pg_stats.most_common_vals/freqs, but for multiple columns.
Statistics improves the estimation of condition selectivity.
Speaker notes
The list of the most common value combinations enables the storage of multiple value combinations and their frequencies.
The slide shows possible city-river pairs. It’s clear that the predicates with the city and river columns are correlated, but unlike the previous example, neither column determines the other — there’s a many-to-many relationship between cities and rivers. In this case, functional dependency statistics won’t help improve the estimates.
QUERY PLAN
-------------------------------------------------------------------------------------
Seq Scan on flights (cost=0.00..5847.00 rows=5429 width=63)
Filter: ((departure_airport = 'LED'::bpchar) AND (aircraft_code = '321'::bpchar))
(2 rows)
The estimate has now improved.
The default_statistics_target parameter controls the number of most frequent values collected. You can configure it for individual statistics:
QUERY PLAN
-----------------------------------------------------------------------------------------
Seq Scan on flights (cost=0.00..5847.00 rows=5088 width=63) (actual rows=5148 loops=1)
Filter: ((departure_airport = 'LED'::bpchar) AND (aircraft_code = '321'::bpchar))
Rows Removed by Filter: 209719
(3 rows)
The cardinality estimate has improved slightly.
However, this did not result in further plan improvements, so increasing the statistics target in this case is likely not justified.
You can view the statistics for the most frequent combinations as follows:
{#source-figure-010 caption=“Source slide page 10: Unique Value Combinations”}
Count of Unique Value Combinations (ndistinct)
Similar to pg_stats.ndistinct, but for multiple columns
Statistics improves the estimation of cardinality for grouping.
Speaker notes
The number of unique value combinations enables more accurate cardinality estimates when grouping by multiple columns.
In the example on the slide, the number of possible combinations of all fields cannot be determined by simply multiplying the unique counts for each column.
Another scenario where the planner miscalculates the estimate arises with grouping. The number of airport pairs connected by direct flights is limited:
QUERY PLAN
---------------------------------------------------------------------
HashAggregate (cost=5847.01..5955.16 rows=10816 width=8)
Group Key: departure_airport, arrival_airport
-> Seq Scan on flights (cost=0.00..4772.67 rows=214867 width=8)
(3 rows)
Extended statistics can improve this estimate (if the type of statistics is not specified, all supported types will be included in the created object):
Statistics enhances the estimation of selectivity for conditions using
expressions.
Speaker notes
Extended Statistics on Expressions extended Statistics on Expression enables the collection of all basic statistics that would be gathered if a column computed using this expression existed in the table.
If the predicate uses an expression instead of a column name on either side of the operator, the planner uses a fixed selectivity estimate. Using expression-based statistics, this issue can be resolved.
Note that expressions can also be used instead of column names in any type of multivariate statistics.
Page 14: Expression Statistics
As discussed in the “Basic Statistics” section, if an expression is present in the condition, the planner, without selectivity information, defaults to a constant and frequently errs:
In the “Basic Statistics” section, we improved the situation by building an expression index, but this isn’t always feasible. Additionally, indexes require storage space and ongoing maintenance. Another approach is to add expression statistics:
Using the commands from the demo, create statistics such as dependencies, mcv, and ndistinct for the flights table. Measure the execution time of the ANALYZE command. Remove the extended statistics, measure the execution time of the ANALYZE command once more, and compare it to the previous result. 2. Write a query to select all business class flights priced over 100,000 rubles. Will extended statistics help improve the cardinality estimate for the result? If so, which type of statistics is better to use?
Speaker notes
Measure the execution time multiple times and average the results to smooth out irregularities.
Page 17: 1. Cost of Analysis
Let’s create extended statistics in the same way as demonstrated.
Functional Dependency Statistics
The initial analysis run may take significantly longer than usual.
=> ANALYZE flights;
ANALYZE
Time: 560.744 ms
=> ANALYZE flights;
ANALYZE
Time: 548.848 ms
=> \timing off
Timing is off.
Remove the created extended statistics:
=>DROPSTATISTICSflights_dep;
DROPSTATISTICS
=>DROPSTATISTICSflights_mcv;
DROPSTATISTICS
=>DROPSTATISTICSflights_nd;
DROPSTATISTICS
Let’s measure the run time again:
=> \timing on
Timing is on.
=> ANALYZE flights;
ANALYZE
Time: 351.717 ms
=> ANALYZE flights;
ANALYZE
Time: 372.846 ms
=> \timing off
Page 18: 2. Using Extended Statistics
Timing is off.
Only three extended statistics were generated, yet the analysis time before and after deletion varied significantly. As the number of collected extended statistics increases, the analysis time also increases, which can result in higher server load.
Therefore, extended statistics should be used judiciously and only when needed.
Business class flights priced over 100,000 RUB:
The optimizer is off by an order of magnitude. The reason is that ticket price and service class are correlated, so extended statistics should improve the estimate. Since there’s no direct functional dependency between the columns, we’ll add statistics for the most frequent values:
QUERY PLAN
------------------------------------------------------------------------------------------
-------
Gather (cost=1000.00..124587.55 rows=12048 width=32)
Workers Planned: 2
-> Parallel Seq Scan on ticket_flights (cost=0.00..122382.75 rows=5020 width=32)
Filter: ((amount > '100000'::numeric) AND ((fare_conditions)::text =
'Business'::text))
(4 rows)
The estimate has improved slightly, but it still differs significantly from the exact value. You can see that the proportion of rows meeting the condition is just over 1%:
Because the default setting stores 100 most frequent pairs, it’s likely that only 1-2 of them will meet the condition, making the optimizer’s estimate inaccurate. To improve accuracy, let’s increase the statistics volume: