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.
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.