https://www.postgresql.org/docs/current/sql-createstatistics.html !!! alter table ... alter column ... set statistics [0..10000] -- columns of hystogram (max 10000 per column) https://www.postgresql.org/docs/current/planner-stats.html https://www.postgresql.org/docs/current/multivariate-statistics-examples.html 2023 Otus - Anton Zolotov - Statistics usage for Query Optimization of 1:55:27 https://www.youtube.com/watch?v=yNAbEtnFikI ! 14:00 - create table ... with(autovacuum_enabled=false); ! 19:00 - select reltuples, relpages from pg_class where relname=''; ! -1 0 ! 26:00 - pg_statistics/pg_stats view ! null_frac, n_distinct -- negative/positive values - are real-num/fraction ! 28:00 - while analyze, table is checked not fully (default_statistics_target (100) * 300 = 300 000 is statistically good number of rows) ! clustered idx - data is ordered ! correlation - data is ordered asc, 0 - not orered, -1 - desc ! most_common_vals - array (of 100) ! most_common_freqs - array as well (of 100) CREATE EXTENSION pgstattuple; SELECT * FROM pgstattuple('table-name') \gx -[ RECORD 1 ]------+-------- table_len | 4427680 tuple_count | 100000 tuple_len | 3368895 tuple_percent | 76.61 dead_tuple_count | 0 dead_tuple_len | 0 dead_tuple_percent | 0 free_space | 16552 free_percent | 0.37 tuple_percent - percent of useful info free_percent - .. of free (old row versions and free space) dead_tuple_count - quality of autovacuum SELECT relname, n_live_tup, n_dead_tup FROM pg_stat_user_tables/pg_stat_all_tables; SELECT relname AS "table", n_live_tup AS live_rows, n_dead_tup AS dead_rows, ROUND((n_dead_tup::float8 / NULLIF(n_live_tup + n_dead_tup, 0)) * 100, 2) AS dead_proc FROM pg_stat_user_tables WHERE n_live_tup + n_dead_tup > 0 ORDER BY dead_proc DESC; SELECT relname, last_autovacuum, last_autoanalyze FROM pg_stat_user_tables WHERE relname="";