Postgres从pg_stats查询列非空去重估计值为负数问题排查
PostgreSQL查询pg_stats返回负数distval问题解答
错误根源
你查询返回负数的核心原因是对pg_stats系统表中n_distinct字段的官方定义理解有误:
- 当
n_distinct为正数时,才代表该列预估的固定非空去重值数量 - 当
n_distinct为负数时,其绝对值代表该列预估的非空去重值占表总行数的比例。比如你拿到的-0.86514354,就代表该列去重值约等于表总行数的86.5%,PostgreSQL返回负数的场景通常是表数据仍在持续增长,优化器判定该列去重值会随表行数同步增长,不会固定为某一常量。
修正方案
要得到正整数格式的distval,你需要关联pg_class系统表拿到对应表的预估行数reltuples,根据n_distinct的正负做换算即可,修正后的查询语句如下:
SELECT s.tablename, s.attname, -- 根据n_distinct的正负换算得到正整数格式的去重值预估值 CASE WHEN s.n_distinct > 0 THEN s.n_distinct::bigint ELSE ABS(s.n_distinct) * c.reltuples::bigint END AS distval, (s.histogram_bounds::text::varchar[])[1] AS min, (s.histogram_bounds::text::varchar[])[array_upper(s.histogram_bounds::text::varchar[], 1)] AS max FROM pg_stats s INNER JOIN information_schema.columns col ON s.tablename = col.table_name AND s.attname = col.column_name INNER JOIN pg_class c ON c.relname = s.tablename AND c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = s.schemaname) WHERE NOT (col.table_schema LIKE 'pg_%' OR col.table_schema = 'information_schema') AND col.data_type = 'character varying' AND col.table_name NOT IN (SELECT viewname FROM pg_views) LIMIT 5;
补充说明
- 上述计算得到的
distval是PostgreSQL基于统计信息生成的预估值,不是精确计数,要提升准确性可以先执行ANALYZE 你的表名更新统计信息 - 原查询使用的隐式连接逻辑已替换为显式INNER JOIN,可读性和可维护性更好
内容的提问来源于stack exchange,提问作者Anđela Todorović
相关产品推荐
相关产品推荐

