UNION ALL字段对齐必须手动完成,各子查询列数、顺序、类型须完全一致;需显式转换类型、清洗字符串、桥接跨库数据并规范别名,否则视图将报错或返回错误结果。

UNION ALL 字段对齐必须手动做,不能依赖数据库自动适配
直接用 SELECT * 拼多个业务系统的表进视图,99% 会失败或出错。PostgreSQL 和 SQL Server 会报类型不匹配错误;MySQL 虽能硬扛过去,但字段顺序错位、NULL 被静默丢弃、字符串截断等问题会在下游暴露。
关键动作是:每个子查询的字段数、顺序、类型三者必须完全一致。少一列就补 NULL::TEXT(PostgreSQL)或 CAST(NULL AS VARCHAR(50))(SQL Server);多一列就得删掉或挪到别处。
- 时间字段统一转成标准类型:
TO_TIMESTAMP(created_str, 'YYYY-MM-DD HH24:MI:SS')(PG)、CONVERT(DATETIME, created_str)(SQL Server) - 数字字段加显式精度:
CAST(amount AS DECIMAL(18,2)),避免隐式转换导致小数丢失 - 枚举字段必须映射标准值:
CASE WHEN LOWER(status) = 'act' THEN 'active' ELSE 'inactive' END AS status
字符串清洗必须在视图里完成,不能甩给应用层
电话号、订单号、地址这类字段,不同系统存法五花八门:有的带括号和短横线,有的纯数字,有的前面还带国家码。如果不在视图里统一,下游 BI 或接口调用时就会匹配失败、去重不准、报表对不上。
清洗逻辑要写死在 SELECT 表达式里,而不是靠注释或文档“提醒”别人处理。
- 电话标准化:
REPLACE(REPLACE(REPLACE(phone, '+86', ''), '-', ''), '(', '') - 订单号补零对齐:
LPAD(RTRIM(order_code), 12, '0') - 大小写归一:
LOWER(category)或UPPER(source_system)
跨库数据必须先桥接,视图本身不解决连接问题
CREATE VIEW 只是一条保存的 SELECT,它运行在哪,就只能访问那个数据库实例能连上的对象。想把 MySQL 用户表和 Oracle 订单表塞进同一个视图?语法上就不通——视图不跨引擎。
真正可行的路径只有一条:让数据先落到同一实例里。
- 同构跨库(如 SQL Server 多库):
db2.dbo.orders这种三段式写法可直接引用 - PostgreSQL 接其他 PG 实例:用
postgres_fdw+IMPORT FOREIGN SCHEMA,注意远端字段类型要和本地声明一致 - MySQL 接其他 MySQL:启用
FEDERATED引擎建远程表,但慎用——所有 WHERE 都拉全量回本地执行,大数据量直接卡死 - 异构场景(MySQL + PostgreSQL + Oracle):必须通过 ETL 工具落地到数仓(如 ClickHouse / StarRocks / 甚至只是同一 PostgreSQL 实例),再建视图
别名命名必须严格,否则 ORM 和 BI 工具会解析失败
字段别名不是可选项,是强制项。没 AS 的列,在 UNION ALL 中可能被取错名;含空格或连字符的别名(如 user-id、order date)会让大多数 ORM(Django、SQLAlchemy)、BI 工具(Tableau、Superset)无法识别列,报 column not found。
统一命名规则比你想象中更重要:所有“用户ID”都叫 user_id,别一个系统叫 uid、一个叫 customer_id、一个叫 member_no。
- 必须显式声明:
created_at AS event_time,不能只写created_at - 禁止特殊字符:
user_id✅,user-id❌,user id❌ - 时间字段统一后命名要一致:
event_time或occurred_at,不要混用
VARCHAR 时间字符串直接 UNION 到 TIMESTAMP 列里,PG 会拒绝,SQL Server 可能静默转成 1900-01-01,而你根本不知道。

















