MySQL 5.7+ 支持虚拟生成列,必须显式声明 VIRTUAL,仅动态计算不存储,不可插入更新,支持索引但不能为主键或 NOT NULL(除非表达式保证非空),添加为瞬时操作,但复杂表达式会降低查询性能。

MySQL 5.7+ 才支持虚拟生成列,且必须用 STORED 或 VIRTUAL 明确指定类型;不加关键字会报错。
创建表时定义虚拟生成列要显式写 VIRTUAL
虚拟生成列不会实际存储数据,只在查询时动态计算。定义时必须带 GENERATED ALWAYS AS 和 VIRTUAL 关键字,否则 MySQL 会默认当成 STORED(物理存储),或直接报语法错误。
常见错误现象:ERROR 1901 (HY000): Function or expression 'xxx' cannot be used in a generated column,多因表达式含非确定性函数(如 NOW()、RAND())或子查询。
- 只允许使用确定性函数、列引用、常量、运算符组合
- 不能引用其他生成列(除非已定义在前)
- 索引可以建在
VIRTUAL列上,但该列本身不可为PRIMARY KEY或含NOT NULL约束(除非表达式保证非空)
CREATE TABLE orders ( id INT PRIMARY KEY, price DECIMAL(10,2), tax_rate DECIMAL(3,2) DEFAULT 0.08, total_price DECIMAL(10,2) GENERATED ALWAYS AS (price * (1 + tax_rate)) VIRTUAL );
给已有表添加虚拟生成列要用 ADD COLUMN ... VIRTUAL
ALTER 表时不能省略 VIRTUAL,否则 MySQL 会认为你要建普通列或 STORED 列,导致行为不符合预期。
使用场景:比如日志表中想快速提取 created_at 的日期部分,又不想冗余存一份 date_only 字段。
- 添加前确认表达式中所有被引用列已存在且类型兼容
- 如果原表数据量大,
ADD COLUMN VIRTUAL是瞬时操作(不重写表),比STORED快得多 - 注意字符集影响:若基于字符串列生成,
COLLATION会继承源列,必要时用CONVERT(... USING ...)显式转换
ALTER TABLE logs ADD COLUMN log_date DATE GENERATED ALWAYS AS (DATE(created_at)) VIRTUAL;
VIRTUAL 列无法在 INSERT/UPDATE 中赋值,但可出现在 SELECT 和 WHERE 中
虚拟列是只读的,试图插入或更新会报错:ERROR 3105 (HY000): The value specified for generated column 'xxx' is not allowed.
但它能参与查询优化——只要表达式简单、确定,MySQL 会下推条件、走索引(如果该列上有索引)。
- WHERE 条件中用虚拟列等价于把表达式展开,例如
WHERE total_price > 100实际执行为WHERE (price * (1 + tax_rate)) > 100 - 可在虚拟列上建普通索引(B-tree),但不能建主键、唯一约束(除非配合
NOT NULL且表达式绝对非空) - JSON 列不能直接用于生成列表达式,需先用
JSON_EXTRACT提取标量值
真正容易被忽略的是:虚拟列的表达式在每次查询时都会重新计算,如果逻辑复杂(比如嵌套函数调用多层、涉及字符串分割),可能拖慢 SELECT 性能——它不是缓存,也不是视图,就是实时算。别指望它“免费加速”,得先看表达式够不够轻量。


















