union all更适合跨年份分区视图,因其跳过去重、性能高且语义准确;而union会因隐式类型转换导致数据丢失或行数减少,且增加cpu开销。

为什么UNION ALL比UNION更适合跨年份分区视图
因为分区视图通常按年份切分表(如 sales_2022、sales_2023、sales_2024),数据天然无重叠,UNION ALL跳过去重逻辑,性能高且语义准确。用 UNION 不但多耗 CPU 做重复扫描,还可能因隐式类型转换意外丢数据。
常见错误现象:UNION 在大表上执行超时,或返回行数少于各子表之和——往往是因为某张分区表的 status 字段是 VARCHAR,另一张是 CHAR(10),导致去重时值被截断或补空格后“看似相同”。
- 始终用
UNION ALL,除非你明确需要合并后去重 - 确保所有
SELECT子句列数、顺序、类型兼容(尤其注意NULL和默认值) - 在每张分区表末尾加注释说明年份,方便后续维护:
-- 2023
列对齐必须显式声明,不能依赖 SELECT *
SELECT * 在分区表结构不一致时会直接报错(如 sales_2023 多了个 discount_type 字段),或静默错位(字段名相同但顺序不同)。视图定义一旦固化,后续加字段就容易崩。
正确做法是把所有列名写全,并对缺失字段补 NULL 或默认值:
CREATE VIEW sales_all AS SELECT id, order_date, amount, '2022' AS year, NULL::TEXT AS discount_type FROM sales_2022 UNION ALL SELECT id, order_date, amount, '2023' AS year, discount_type FROM sales_2023 UNION ALL SELECT id, order_date, amount, '2024' AS year, discount_type FROM sales_2024;
- 每列类型要对齐:比如
amount在 2022 表是NUMERIC(10,2),2023 表是DECIMAL(12,2),没问题;但若一个是FLOAT、一个是INT,某些数据库(如 PostgreSQL)会强制转为DOUBLE PRECISION,精度可能出问题 - 别省略别名:即使源字段名一致,也要统一用
AS显式命名,避免未来某张表改名导致视图失效 -
year字段强烈建议加,它不只是标识,更是后续WHERE year IN ('2023', '2024')下推过滤的关键
PostgreSQL / MySQL / SQL Server 对分区视图的支持差异
不是所有数据库都允许优化器下推条件到 UNION ALL 子查询。这意味着你加了 WHERE year = '2024',某些引擎仍会扫全部三张表。
- PostgreSQL 12+ 支持较完善:只要每层
SELECT有常量表达式(如'2024' AS year),且查询条件能匹配该常量,就会跳过其他分支 - MySQL 8.0+ 需配合
EXPLAIN FORMAT=TREE确认是否裁剪;若某张表用了YEAR(order_date)动态算年份,就无法裁剪 - SQL Server 要求更严:必须每张表都有
CHECK约束(如CONSTRAINT chk_year CHECK (year = '2024')),否则不走分区消除
验证方法很简单:EXPLAIN 看执行计划里是否只出现目标年份的表名,而不是全部列出。
容易被忽略的 NULL 处理和统计函数陷阱
跨年份视图上直接跑 COUNT(*) 没问题,但 COUNT(amount) 会忽略所有 NULL 值——如果某张分区表的历史数据没补 amount,而其他表补了默认值,结果就不可比。
- 统一补
COALESCE(amount, 0)再聚合,或在视图里提前处理 -
AVG()对NULL更敏感:一张表 100 行其中 10 行NULL,另一张表 50 行全非空,混合后平均值权重就偏了 - 时间范围查询别只靠
order_date BETWEEN ...:如果某张表的order_date有脏数据(比如 2025 年的订单误入 2023 表),year字段才是可信锚点
最麻烦的是排序:如果没加 ORDER BY,UNION ALL 结果顺序完全不确定,尤其跨多个物理文件时,每次查询返回顺序可能不同——这在分页场景下会导致重复或漏数据。










