虚拟列是基于其他列动态计算的逻辑列,不占磁盘空间,查询时实时求值;STORED列则在写入时计算并物理存储,支持索引、外键等。
虚拟列(Generated Column)不是真实存储的数据字段,而是基于表中其他列通过表达式动态计算得出的逻辑列。它的核心原理是“定义即计算”,不占磁盘空间,查询时实时求值;而派生列若设为 STORED 类型,则属于物理存储的派生列,在写入或更新时完成计算并落盘。
当你定义一个 VIRTUAL 生成列,比如:
CREATE TABLE logs (id INT, content TEXT, created_at DATETIME, date_only DATE GENERATED ALWAYS AS (DATE(created_at)) VIRTUAL);
MySQL 并不会在磁盘上为 date_only 分配空间。每次执行 SELECT date_only FROM logs,引擎都会现场调用 DATE(created_at) 计算该行结果。这意味着:
STORED(也称 PERSISTENT)生成列才具备“派生列”的完整存储语义。它在 INSERT/UPDATE 时触发计算,并把结果像普通列一样写入聚簇索引或二级索引页中。例如:
ALTER TABLE orders ADD COLUMN amount_with_tax DECIMAL(12,2) GENERATED ALWAYS AS (amount * 1.08) STORED;
这个值被真正存下来,因此可以:
MySQL 明确禁止虚拟列引用另一个虚拟列,例如:
-- ❌ 错误:不能在 virtual_col2 的表达式中引用 virtual_col1
col1 INT,
virtual_col1 VARCHAR(20) GENERATED ALWAYS AS (UPPER(LEFT(col1, 3))) VIRTUAL,
virtual_col2 VARCHAR(20) GENERATED ALWAYS AS (CONCAT(virtual_col1, '_suffix')) VIRTUAL
这是因为虚拟列无存储实体,其值仅在 SELECT 执行阶段存在,DDL 解析时无法解析依赖链。但允许嵌套函数调用,如 UPPER(SUBSTRING(email, 1, LOCATE('@', email)-1)) 是合法的。
表面看虚拟列每次都要算,似乎拖慢查询,但它真正的价值在于“让表达式可索引”。例如:
WER(email) = 'admin@test.com' 无法使用 email 上的普通索引