分表结果合并统计
按时间或业务把数据拆成多张表之后,跨分片的汇总统计要靠合并:把各个分片的结果用不去重的合并拼成一张逻辑表,再在外层做分组聚合。有两个必须守住的前提:各分支的列数与顺序要一致、类型要兼容;聚合必须发生在合并之后,否则平均值、去重计数这类指标算出来的口径是错的。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| 合并方式 | 不去重的合并保留全部分片行,适合统计 |
| 结构要求 | 分支列数一致、顺序对应、类型可兼容 |
| 聚合位置 | 必须放在合并后的外层 |
| 分片建议 | 命名带时间后缀,便于拼接与后续归档 |
典型使用场景
- 按月分表的订单表做年度汇总统计
- 按业务线分库的数据合并成统一报表
- 分片数据做去重计数:必须整体去重后再计数
- 历史分片归档前的核对:合并后行数与预期比对
- 跨分片的平均值计算:不能对各分片平均值再求平均
合并与聚合示例
合并与聚合示例
-- 先把分片拼成逻辑表,再在外层聚合
SELECT city, SUM(amount) AS total, COUNT(*) AS cnt
FROM (
SELECT city, amount FROM orders_2023
UNION ALL
SELECT city, amount FROM orders_2024
) t
GROUP BY city
ORDER BY total DESC;
-- 分片类型不一致时显式对齐,避免隐式转换
-- SELECT city, CAST(amount AS DECIMAL(18,2)) FROM orders_2023
-- 跨分片求平均:用总量除以总行数,不要对分片平均值再平均
SELECT SUM(total) / SUM(cnt) AS avg_amount
FROM (
SELECT SUM(amount) AS total, COUNT(*) AS cnt FROM orders_2023
UNION ALL
SELECT SUM(amount) AS total, COUNT(*) AS cnt FROM orders_2024
) s;
-- 跨分片去重计数:先合并再整体去重
SELECT COUNT(DISTINCT user_id)
FROM (SELECT user_id FROM orders_2023
UNION ALL
SELECT user_id FROM orders_2024) u;分片合并要点
| 关注点 | 做法 | 常见错误 |
|---|---|---|
| 列结构 | 列数与顺序对齐,类型显式转换 | 依赖隐式转换,取值被截断 |
| 行合并 | 使用不去重形式保留全部分片行 | 用去重形式导致重复数据被合并 |
| 聚合位置 | 合并之后在外层聚合 | 先聚合再合并,口径错误 |
| 平均值 | 总量除以总行数 | 对分片平均值再求平均 |
| 去重计数 | 先合并再整体去重 | 把分片去重结果相加 |
平均值的错误最隐蔽:对各分片的平均值再求平均,得到的是一个与真实平均无关的数字,而且量级看起来很正常。分片行数差异越大,偏差越离谱。
分片数量经常增长时,手工拼接的合并语句会越来越难维护。可以考虑用视图把分片统一暴露成一张逻辑表,业务查询只针对视图编写。