用 UNION ALL + 视图是最简单可靠的方案,但必须显式处理字段类型对齐、时间字段语义统一和 NULL 值隐式转换问题;历史表与实时表存在类型不一致、时间字段名/精度不同、状态字段语义冲突三类隐性不兼容,需逐字段 CAST 和重命名,加 source_type 辅助调试,且视图不解决数据重复或空洞问题,须靠归档规则和追加写语义保障一致性。

直接说结论:用 UNION ALL + 视图是最简单可靠的方案,但必须显式处理字段类型对齐、时间字段语义统一和 NULL 值隐式转换问题。
为什么不能直接 CREATE VIEW 就完事?
历史表(比如按月分区的 sales_202310、sales_202311)和实时表(比如 sales_current)往往存在三类隐性不兼容:
- 字段顺序一致但类型不一致——例如历史表中
amount是DECIMAL(10,2),实时表里是FLOAT,某些数据库(如 PostgreSQL)在UNION时会报错; - 时间字段名或含义不同——历史表用
event_time,实时表用created_at,且后者可能含微秒精度,前者只有秒级; - 实时表有未同步到历史表的临时状态字段(如
status = 'pending'),而历史表该字段固定为'done',直接UNION会导致语义混淆。
UNION ALL 视图必须显式 CAST 和重命名
不要依赖数据库自动类型推导。以 PostgreSQL 为例,下面这种写法大概率失败:
CREATE VIEW sales_combined AS SELECT * FROM sales_202310 UNION ALL SELECT * FROM sales_current;
正确做法是逐字段控制:
CREATE VIEW sales_combined AS SELECT id::BIGINT, CAST(amount AS DECIMAL(10,2)) AS amount, event_time::TIMESTAMP WITHOUT TIME ZONE AS event_time, 'historical'::TEXT AS source_type FROM sales_202310 UNION ALL SELECT id::BIGINT, CAST(amount AS DECIMAL(10,2)) AS amount, created_at::TIMESTAMP WITHOUT TIME ZONE AS event_time, 'realtime'::TEXT AS source_type FROM sales_current;
关键点:
- 所有对应列必须同类型,
CAST比隐式转换更可控; - 时间字段统一别名为
event_time,避免下游 SQL 写COALESCE(event_time, created_at); - 加
source_type字段方便调试和权限隔离,不是可选装饰项。
实时表数据延迟导致的“重复”与“空洞”问题
视图本身不解决数据一致性,它只是把两套数据拼在一起展示。常见陷阱:
- 实时表刚插入一条记录,但尚未归档进历史表——此时视图查出来是 1 条;等归档任务跑完,同一条记录在历史表+实时表各出现一次,视图就变成 2 条;
- 归档任务失败或滞后,某天的数据只存在于实时表,但业务查询按日期范围扫,结果看起来“断了一天”;
- 实时表有
ON CONFLICT DO UPDATE场景,历史表却是只读,导致同一id在两个来源中值不一致。
应对方式不是改视图,而是提前约定规则:
- 归档脚本必须带
DELETE FROM sales_current WHERE event_time < '2023-11-01',确保无重叠; - 视图外层建议包一层
ROW_NUMBER() OVER (PARTITION BY id ORDER BY event_time DESC)去重逻辑(如果业务能接受最新版本优先); - 禁止在实时表做
UPDATE—— 改用INSERT+ 逻辑删除标记,保持追加语义。
MySQL 8.0+ 和 Hive 的特殊处理
MySQL 不支持在视图定义里写子查询别名(如 (SELECT ...) t),所以 UNION ALL 必须扁平写;Hive 则不支持视图中引用非当前数据库的表,除非用 db_name.table_name 全限定名。
另外 Hive 视图默认不物化,每次查都重跑底层表扫描——如果历史表是 TB 级,务必确认执行引擎(Tez/Spark)已开启谓词下推,否则 WHERE event_time > '2023-11-01' 可能仍扫全量历史分区。
真正难的不是语法,是让 DBA 和实时数仓同学对齐“哪天的数据算‘已归档’”,这个边界一旦模糊,视图再漂亮也查不准。

















