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