MySQL中模拟多态的合理方案是:①用UNION ALL视图对齐字段并显式类型转换;②差异大时用JSON字段+生成列索引;③优先应用层路由而非数据库抽象;④避免EAV模型。

用视图 + UNION ALL 模拟多态查询
MySQL 本身不支持面向对象意义上的“多态”,但可以通过统一查询接口让不同结构的表对外表现一致。核心思路是:为每张表写一个字段对齐的 SELECT,再用 UNION ALL 合并,最后封装成视图。
常见错误是忽略字段类型兼容性,比如一张表的 amount 是 DECIMAL,另一张是 VARCHAR,直接 UNION 会触发隐式转换或报错。
- 所有
SELECT的列数、顺序、类型必须严格一致;类型不同时显式用CAST(... AS ...)转换 - 给每张表加一个固定标识字段(如
'order' AS source_type),方便后续区分来源 - 避免用
UNION(去重开销大),一律用UNION ALL - 视图中不要写
WHERE或LIMIT,这些应下推到应用层或外部查询中
CREATE VIEW unified_events AS SELECT id, user_id, CAST(amount AS DECIMAL(12,2)) AS amount, created_at, 'order' AS source_type FROM orders UNION ALL SELECT id, user_id, CAST(total AS DECIMAL(12,2)) AS amount, paid_at AS created_at, 'payment' AS source_type FROM payments UNION ALL SELECT id, target_id AS user_id, CAST(fee AS DECIMAL(12,2)) AS amount, occurred_at AS created_at, 'fee' AS source_type FROM fees;
用 JSON 字段存异构数据 + 通用表结构
当各业务实体差异过大(比如字段数量、含义、增删频繁),硬对齐字段成本高。这时可退一步:只保留公共骨架字段,把差异化部分收进 JSON 类型字段。
MySQL 5.7+ 原生支持 JSON 类型,并提供 JSON_EXTRACT、->、->> 等函数查询,但要注意性能边界——JSON 字段无法直接建普通索引,需配合生成列 + 函数索引。
- 主表至少保留
id、type(标识子类)、data JSON、created_at等通用字段 - 查询常用 JSON 字段时,定义生成列并建索引,例如:
ALTER TABLE events ADD COLUMN status VARCHAR(20) AS (data->>"$.status"); CREATE INDEX idx_status ON events(status); - 避免在
WHERE中对data做复杂路径匹配,尤其是嵌套深或带数组的场景,容易全表扫描
应用层做“多态路由”而非数据库层模拟
很多团队过早尝试在 SQL 层抽象多态,结果视图越来越重、JOIN 越来越深、慢查频发。其实更务实的做法是:数据库保持分表自治,把“统一操作”的逻辑移到应用代码里。
比如 Go/Python 中定义一个接口或基类,每个具体表对应一个实现,共用同一套调用入口:
- 查询时,根据
type参数决定查哪张表,再调用对应 DAO 方法 - 写入时,先校验
type是否合法,再路由到对应INSERT语句 - 用配置或注册表管理 type → table / struct 的映射关系,避免硬编码
- 若需跨类型聚合(如“用户最近 10 条操作”),用应用层合并结果,而不是强行 UNION —— 尤其当各表数据量级差异大时
为什么不要用 EAV 模型模拟多态
EAV(Entity-Attribute-Value) 表结构看似灵活,实则在 MySQL 中极易引发性能和维护问题,不是多态的合理解法。
典型症状包括:单个业务查询要 JOIN 十几次、无法使用复合索引、COUNT/SUM 等聚合变慢、SQL 可读性归零。它适合元数据极稀疏且查询极少的场景(如 CMS 自定义字段),不适合高频交易或分析类业务。
- 一旦出现
WHERE attr = 'price' AND value > 100这类条件,MySQL 很难优化,基本等于全表扫描 - 没有数据库级约束,
value字段通常是TEXT或VARCHAR(255),类型丢失,应用层要反复解析 - 想加个新字段?不是改表结构,而是 INSERT 一行新记录——DDL 变 DML,运维和审计都混乱
真正需要灵活性时,优先考虑 JSON 字段或应用层路由,EAV 是最后的选择。










