MySQL不支持表继承,因其关系型设计专注OLTP性能而非OOP建模;替代方案有单一表、类表和EAV模式,其中类表模式需注意主键复用、类型排他性及JOIN安全。

class Employee extends Person,然后期望 MySQL 自动处理字段复用、多态查询或父类约束。
这本质是个建模层与存储层错位的问题:应用逻辑想用 OOP 思维组织数据,但 MySQL 是关系型引擎,只认表、列、外键和索引。
为什么不能直接用“表继承”语法?
MySQL 解析器根本不识别 INHERITS、EXTENDS TABLE 这类关键字(PostgreSQL 有有限的 INHERITS,但 MySQL 完全没有)。任何试图执行类似语句的操作都会报错:
ERROR 1064 (42000): You have an error in your SQL syntax。这不是版本问题,是设计取舍——MySQL 专注 OLTP 场景下的稳定与性能,而非抽象建模能力。
常见替代方案及其适用场景
实际项目中,有三种主流做法,选哪个取决于你的查询模式、变更频率和一致性要求:
-
单一表模式(Single Table Inheritance):所有子类字段塞进一张大表,用
type字段区分角色。适合子类差异小、查询常跨类型(如“查所有活跃用户,不管是不是管理员”),但空字段多、schema 膨胀快。 -
类表模式(Class Table Inheritance):父类一张表(如
users),每个子类一张扩展表(如admins、customers),通过user_id外键关联。适合子类字段多、业务逻辑隔离强,但 JOIN 查询频繁,且无法强制某条记录“只能属于一个子类”。 -
实体-属性-值模式(EAV):把动态字段存成行(
entity_id,attr_key,attr_value)。灵活性高,但几乎放弃 SQL 查询能力、索引失效、难以校验类型,仅适用于配置类极低频读写的场景,不推荐用于核心业务实体。
类表模式下必须注意的细节
如果选类表模式(最接近“继承语义”的折中方案),这些点容易踩坑:
- 父表主键必须同时是子表的主键兼外键,例如:
CREATE TABLE users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100), email VARCHAR(255) ); CREATE TABLE admins ( id BIGINT PRIMARY KEY, role_level TINYINT, FOREIGN KEY (id) REFERENCES users(id) ON DELETE CASCADE );
否则无法保证一对一绑定,也难以做级联删除。 - 应用层需自行维护“类型排他性”。MySQL 不支持 CHECK 约束跨表判断(如“一条记录不能同时在
admins和customers表中存在”),得靠应用逻辑或触发器(但触发器在分布式写入下不可靠)。 - 联合查询时,
LEFT JOIN比INNER JOIN更安全——避免因漏插子表数据导致整条记录消失;但要注意 NULL 值处理逻辑是否符合业务预期。
ORM 层怎么“假装有继承”?
Django ORM、SQLAlchemy、MyBatis Plus 等框架提供的“继承映射”只是语法糖,底层仍是上述某一种物理模型。比如 Django 的 abstract = True 只影响迁移生成,不改变数据库结构;而 multi-table inheritance 就对应类表模式,并自动生成 JOIN 查询。关键点在于:别让 ORM 掩盖了底层 JOIN 成本和约束缺失的事实。一旦需要写原生 SQL 或做性能调优,你得立刻切回真实表结构去思考。










