MySQL 8.0+才支持VIRTUAL生成列,5.7仅支持STORED;VIRTUAL列表达式必须确定性(如price*qty),禁用NOW()、RAND()等非确定函数;不可作主键或外键,但8.0.13+可建索引;插入/更新时不可显式赋值,否则报ERROR 3105。

MySQL 8.0+ 才支持 VIRTUAL 生成列,低版本会直接报错
MySQL 5.7 只支持 STORED 生成列,VIRTUAL 是 8.0 引入的。如果你执行 ALTER TABLE t ADD COLUMN c INT AS (a + b) VIRTUAL 报错 ERROR 1064 或提示语法错误,先查版本:SELECT VERSION();。低于 8.0 的话,这条路走不通,别硬试。
AS (expr) 中的表达式必须是确定性(deterministic)且无副作用
MySQL 要求虚拟列的表达式在相同输入下必须返回相同结果,且不能调用非确定性函数。以下写法全都不合法:
-
AS (NOW())——NOW()每次执行值都不同 -
AS (UUID())—— 同样非确定性 -
AS (RAND())—— 明确被禁止 -
AS (USER())—— 依赖会话上下文
能用的典型例子:AS (price * quantity)、AS (UPPER(name))、AS (CASE WHEN status = 1 THEN 'active' ELSE 'inactive' END)。
虚拟列不能作为主键或外键,但可建索引(仅 8.0.13+ 支持)
你不能写 id INT AS (a + b) VIRTUAL PRIMARY KEY,MySQL 会拒绝。外键同理——VIRTUAL 列不实际存储,无法参与约束校验。但索引是例外:从 8.0.13 开始,允许对 VIRTUAL 列建普通索引(B-tree),例如:
ALTER TABLE orders ADD COLUMN total_amount DECIMAL(10,2) AS (unit_price * qty) VIRTUAL; CREATE INDEX idx_total ON orders(total_amount);
注意:该索引是真实构建的,会占用空间;查询 WHERE total_amount > 100 能命中,但前提是表达式本身支持索引下推(比如不含函数嵌套过深或类型隐式转换)。
插入或更新时不能显式赋值给 VIRTUAL 列,否则报错 ERROR 3105
虚拟列由 MySQL 自动计算,不允许用户干预。下面操作都会失败:
-
INSERT INTO t (a, b, c) VALUES (1, 2, 999);—— 显式指定c值 -
UPDATE t SET c = 100 WHERE id = 1;—— 尝试修改
正确做法是只操作基础列:INSERT INTO t (a, b) VALUES (1, 2);,此时 c 自动按定义表达式算出。如果表结构里已有 VIRTUAL 列,又想批量导入数据,得确保 INSERT 语句中明确排除该列名,或使用 SET sql_mode = ''(不推荐)绕过严格模式——但更稳妥的是改用 LOAD DATA INFILE 并跳过对应字段。
虚拟列真正省事的地方在于「读多写少 + 表达式稳定」的场景,一旦涉及动态值、权限判断或跨表逻辑,就该考虑视图或应用层计算了。


















