虚拟列是表结构的一部分,值由依赖列实时计算得出,非视图或临时字段;mysql和oracle均支持,但stored列可索引、占存储,virtual列不存盘、仅查询时计算,且表达式必须确定性。

GENERATED ALWAYS AS 定义的列,不是视图,也不是临时计算字段——它是表结构的一部分,值由依赖列实时算出,不靠 CREATE VIEW。
MySQL 和 Oracle 都支持虚拟列,但实现逻辑和使用边界差异明显,直接混用会出错。
虚拟列不是视图,别当成 CREATE VIEW 的替代品
视图是查询封装,不改变底层表结构;虚拟列是表的正式列,写入 DDL,参与索引、约束、主键(部分场景),也受 INSERT/UPDATE 语义约束。比如:INSERT INTO t (a, b) VALUES (1, 2) 可以成功,但 INSERT INTO t (a, b, vcol) VALUES (1, 2, 3) 会报错 ERROR 3105 (HY000): The value specified for generated column 'vcol' is not allowed.。
STORED vs VIRTUAL:选错类型直接影响性能和存储
两者都要求表达式必须是确定性的(NOW()、RAND()、UUID() 等非确定函数禁止使用),但行为完全不同:
-
STORED列:值物理落盘,更新依赖列时会同步重算并写入,适合高频查询+低频更新+计算开销大的场景(如JSON_EXTRACT(data, '$.price') * quantity) -
VIRTUAL列:纯内存计算,不占磁盘空间,适合轻量表达式(如SUBSTRING_INDEX(email, '@', -1))或只偶尔查的字段 - MySQL 5.7+ 默认是
VIRTUAL,省略关键字即生效;Oracle 则默认为VIRTUAL,且不支持STORED - 索引只能建在
STORED列上(MySQL),VIRTUAL列加索引会报错ERROR 3106 (HY000): Expression column cannot be indexed.
为什么不用视图而用虚拟列做“计算型视图”?
当目标是让应用代码或 ORM 透明消费计算结果时,虚拟列更可靠:
- ORM 映射友好:
SELECT *能直接拿到subtotal字段,不需要额外定义视图映射或 SQL 片段 - 支持
WHERE下推:对STORED虚拟列加索引后,WHERE subtotal > 100走索引,视图无法做到这点(除非物化) - 约束可作用于虚拟列:例如
subtotal CHECK (subtotal >= 0),视图无法加 CHECK 约束 - 避免视图嵌套陷阱:多层视图可能让执行计划变差,虚拟列始终是单层表达式
容易被忽略的坑:依赖列变更会连锁失效
虚拟列的生命线完全绑定依赖列——这不是 bug,是设计前提,但常被低估:
- 改依赖列名(如把
price改成unit_price),虚拟列定义不会自动更新,查询直接报错Unknown column 'price' in generated column - 删依赖列,整个表 DDL 失效,
ALTER TABLE ... DROP COLUMN price会失败,必须先删虚拟列 - 虚拟列不能引用其他虚拟列(MySQL 报错
ERROR 3104 (HY000): Generated column cannot refer to a generated column.) - Oracle 中自定义函数用于虚拟列时,函数必须声明为
DETERMINISTIC,否则建表失败
真实项目里,虚拟列的价值不在“炫技”,而在把业务规则固化进 schema 层。它比视图更硬,比应用层计算更稳——前提是清楚它的边界在哪。











