mysql 8.0+ 支持直方图统计,通过 analyze table ... update histogram 创建单列分布摘要,存于数据字典供优化器使用;postgresql 需用 width_bucket 手动模拟;sql server 直方图随统计对象自动生成且最多200桶;通用方案为 count + case when 分组。

MySQL 中用 HISTOGRAM 语法直接生成直方图统计
MySQL 8.0+ 原生支持直方图,但不是图形,而是列值分布的统计摘要,用于优化器估算行数。它不返回可视化图表,而是把分布信息存进数据字典,后续 EXPLAIN 会用到。
建直方图用 ANALYZE TABLE ... UPDATE HISTOGRAM,比如:
ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH 16 BUCKETS;
注意:status 必须是已索引列或高基数列,低基数(如只有 3 个取值)效果差;16 是桶数,范围 1–1024,不是越多越好,写入频繁的表慎用——每次更新直方图会锁表几秒。
-
SHOW CREATE TABLE orders查不到直方图定义,得查information_schema.COLUMN_STATISTICS - 直方图不自动更新,数据批量变更后需手动重建
- 只对单列有效,不支持多列组合直方图
PostgreSQL 中用 pg_stats 和 WIDTH_BUCKET 手动模拟
PostgreSQL 没内置直方图语法,但统计信息存在 pg_stats 视图,且可用 WIDTH_BUCKET 分桶做近似直方图查询。
例如查 price 列在 0–1000 区间内 10 等分的频次:
SELECT WIDTH_BUCKET(price, 0, 1000, 10) AS bucket,
COUNT(*) AS freq
FROM products
WHERE price IS NOT NULL
GROUP BY bucket
ORDER BY bucket;
关键点:
-
WIDTH_BUCKET的上下界必须覆盖实际数据范围,否则会出NULL桶或报错 - 若列含大量
NULL,必须显式过滤,否则WIDTH_BUCKET返回NULL,无法分组 - 桶数太多(如 >100)在大数据量下可能慢,建议先用
PERCENTILE_CONT试探分布再定区间
SQL Server 用 DBCC SHOW_STATISTICS 查看现有统计直方图
SQL Server 的统计对象自带直方图(200 个等高桶),但只能查看,不能手动创建新直方图——它是随 CREATE STATISTICS 或自动统计更新时附带生成的。
查某列统计直方图:
DBCC SHOW_STATISTICS('sales', 'IX_sales_product_id');
输出里 Histogram 部分每行是一个桶:包含 RANGE_HI_KEY(桶上限)、EQ_ROWS(等于该值的行数)、RANGE_ROWS(该桶内其他值的行数)。
- 直方图最多 200 桶,超出自动合并相邻桶,导致精度下降
- 如果列上没统计对象,
DBCC报错,得先CREATE STATISTICS - 直方图不包含
NULL值统计,它们单独记在STAT_HEADER的ROWS和ROWS_SAMPLED差值里
通用替代方案:用 COUNT + CASE WHEN 做离散区间统计
所有 SQL 方言都支持的手动分组法,适合快速看分布趋势,不依赖版本特性。
比如看用户年龄分布(0–10、11–20…):
SELECT
CASE
WHEN age BETWEEN 0 AND 10 THEN '0-10'
WHEN age BETWEEN 11 AND 20 THEN '11-20'
WHEN age > 60 THEN '60+'
ELSE 'other'
END AS age_group,
COUNT(*) AS cnt
FROM users
GROUP BY 1;
这个写法看着简单,但容易漏三点:
- 区间边界重叠或遗漏(比如写了
BETWEEN 0 AND 10又写了) - 没处理
NULL,导致整组丢失——加OR age IS NULL或开头单独WHEN age IS NULL - 字符串分组名排序是字典序,
'100+'会排在'20+'前面,必要时用数字前缀或ORDER BY显式控制
直方图本质是分布估算工具,不是可视化手段。真要画图,得把结果导出到 Python / R / Excel;数据库里能做的,就是拿到足够可靠的分桶计数——而最容易被忽略的,永远是 NULL 的归属和边界条件的闭合性。











