First, a remark: your query could be written simpler as
SELECT count(DISTINCT assembly_id) FROM assembly_prods;
Also, your statistics query is wrong, because n_distict
can also be negative. You should query:
SELECT CASE WHEN s.n_distinct < 0
THEN - s.n_distinct * t.reltuples
ELSE s.n_distinct
END AS n_distinct
FROM pg_class t
JOIN pg_namespace n ON n.oid = t.relnamespace
JOIN pg_stats s ON t.relname = s.tablename
AND n.nspname = s.schemaname
WHERE s.schemaname = 'public'
AND s.tablename = 'assembly_prods'
AND s.attname = 'assembly_id';
For a simple query like that, the statistics should contain a good estimate.
If the estimates are off, try to ANALYZE
the table. That will also fix the results for a newly TRUNCATE
d table. TRUNCATE
does not cause PostgreSQL to autoanalyze the table (there may be room for improvement here).
If that improves the results, see that the table is analyzed more often by configuring
ALTER TABLE assembly_prods SET (autovacuum_analyze_scale_factor = 0.05);
It is also possible to set autovacuum_analyze_scale_factor
to 0 and raise autovacuum_analyze_threshold
to the daily change rate for the table.
If ANALYZE
alone does not improve the estimate, increase the size of the sample:
ALTER TABLE assembly_prods ALTER assembly_id SET STATISTICS 1000;
A new ANALYZE
should now produce better estimates.
Getting good n_distinct
estimates for more complicated queries becomes increasingly more difficult. Sometimes extended statistics will improve the estimate considerably.
As far as I know, PostgreSQL v12 does not bring any improvements in this area.