MySQL STORED 生成列(Generated Column) 的使用小结

 更新时间:2026年02月11日 09:47:30   作者:Knight_AL  
MySQL 8中的生成列可以解决带函数判断的SQL导致的索引无法使用和性能问题,生成列分为VIRTUAL和STORED,其中STORED列会在插入时计算并存储在磁盘上,可以建索引,非常适合用于复杂的SQL优化,下面就一起来了解一下

在 MySQL 8 中,如果你经常写带函数判断的 SQL,例如:

WHERE WEEKDAY(creatime) < 5

你会发现:

  • 索引无法使用
  • 执行计划 type = ALL
  • 大表查询慢得像蜗牛

常见的数据计算,比如“是否工作日、是否有效”、“金额是否超过阈值”、“是否逾期”等,都容易写成函数形式,导致索引无法命中。

在高并发、大数据量的场景下,这种写法会拖垮整个系统

解决办法是什么?

👉 MySQL 生成列(Generated Column)+ STORED(存储列) + 索引

一、什么是生成列(Generated Column)

MySQL 的生成列有两种:

类型特点
VIRTUAL 虚拟列不存储,查询时现算
STORED 存储列算完真实写入磁盘,可建索引

生成列的语法:

column_name data_type
GENERATED ALWAYS AS (表达式)
[VIRTUAL | STORED]

例如,根据 creatime 自动计算是否工作日:

is_workday TINYINT
GENERATED ALWAYS AS (
    CASE WHEN WEEKDAY(creatime) < 5 THEN 1 ELSE 0 END
) STORED

二、STORED 与普通字段有什么区别?

很多人不清楚为什么“用 STORED 很香”,下面用一个表格秒懂👇

对比项普通字段STORED 生成列
值由谁计算?开发者自己写入MySQL 根据表达式自动算
更新时是否要维护?要自己维护creatime 改,自动重算
能否防止脏数据?容易写错、漏改保证永远正确
能否建索引?可以可以(而且非常常用)
查询时需不需要重新计算?不需要不需要
写入性能一般插入时计算一次
典型场景普通字段业务派生字段(是否周末、是否逾期、金额区间等)

一句话总结:

STORED = 自动计算的普通字段,可建索引,是 SQL 优化神器。

三、为什么 STORED 列可以让 SQL 飞起来?

来看经典错误写法:

WHERE WEEKDAY(creatime) < 5

你对 creatime 做了函数:

  • creatime 索引用不了
  • 强制全表扫
  • 大数据量直接炸

而 STORED 生成列写法:

WHERE is_workday = 1

它是普通字段:

  • 可以建索引
  • 非常高效
  • 查询极快

MySQL 查询优化器最喜欢:

字段 = 常量
字段 BETWEEN 区间
字段 IN (...)

生成列完美契合这一点。

四、一个医院真实业务案例:统计工作日到访人数

医院表 t_visit

CREATE TABLE t_visit (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    hospital_id INT,
    creatime DATETIME,
    visit_num INT
);

需求:

统计各医院在工作日(周一到周五)的就诊人数

错误写法:索引完全失效!

SELECT SUM(visit_num)
FROM t_visit
WHERE WEEKDAY(creatime) < 5;

解释:

  • Creatime 上套函数 → 索引失效
  • 查询 100W 行 → 全表扫描
  • 业务卡死

五、使用 STORED,企业级写法来了

1)添加生成列

ALTER TABLE t_visit
  ADD COLUMN is_workday TINYINT
    GENERATED ALWAYS AS (
      CASE WHEN WEEKDAY(creatime) < 5 THEN 1 ELSE 0 END
    ) STORED,
  ADD INDEX idx_visit_workday (is_workday, creatime);

2)正确查询写法

SELECT
    hospital_id,
    SUM(visit_num)
FROM t_visit
WHERE
    is_workday = 1
    AND creatime BETWEEN '2025-01-01' AND '2025-02-01'
GROUP BY hospital_id;

EXPLAIN 显示:

  • type = range
  • key = idx_visit_workday
  • 几万行 → 几千行
  • 性能提升 5~30 倍

六、为什么企业更喜欢 STORED 而不是 VIRTUAL?

维度VIRTUALSTORED
存储方式不落盘落盘
查询成本每查都计算不需要计算
能否建 index老版本不支持、多版本有限制全版本支持,生产常用
性能适合小数据适合大数据、OLTP、高并发

大量业务都在用:

  • 是否工作日
  • 是否节假日
  • 是否逾期
  • 是否有效
  • 金额区间分类(如大单、中单、小单)
  • 年龄段分类
  • 设备状态派生字段

只要是某列可以推导出来的值,且要做过滤、排序、聚合,80% 的情况下会用 STORED。

七、STORED 生成列 + dim_date = 双剑合璧最强方案

在 BI / 数仓中常用维表:

CREATE TABLE dim_date (
    date_key DATE PRIMARY KEY,
    weekday TINYINT,
    is_workday TINYINT,
    is_holiday TINYINT,
    holiday_name VARCHAR(20)
);

事实表:

ALTER TABLE t_visit
ADD visit_date DATE GENERATED ALWAYS AS (DATE(creatime)) STORED,
ADD INDEX (visit_date);

查询:

SELECT
    v.hospital_id,
    SUM(v.visit_num)
FROM t_visit v
JOIN dim_date d ON v.visit_date = d.date_key
WHERE 
    d.is_workday = 1
GROUP BY v.hospital_id;

优势:

  • 超高性能
  • 法定节假日、调休随便改
  • 报表、看板、数据集市都复用 dim_date
  • 企业统一口径

八、生产注意事项

  1. 生成列不能手工 INSERT / UPDATE
  2. 表插入非常频繁时,STORED 会多一次计算成本(但一般可以接受)
  3. 表过大时,修改表结构添加 STORED 列要注意线上压力
  4. 建立索引时一定要注意前导列(选择性越高越好)
  5. 如果你的计算很复杂,可以考虑 STORED + 函数表达式预处理

九、总结:一句话记住 STORED

STORED 生成列,是 MySQL 自动计算、自动维护、可建索引的派生字段。
它让复杂 SQL 拆分成“插入时算一次,查询时用高速索引”,
是 OLTP 性能优化最常用、最实用也最容易被忽略的武器。

到此这篇关于MySQL STORED 生成列(Generated Column) 的使用小结的文章就介绍到这了,更多相关MySQL STORED 生成列内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL GROUP BY分组取字段最大值的方法示例

    MySQL GROUP BY分组取字段最大值的方法示例

    本文介绍了如何使用MySQL的GROUPBY语句结合MAX函数来实现分组取字段最大值的操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-01-01
  • mysql的约束及实例分析

    mysql的约束及实例分析

    这篇文章主要介绍了mysql的约束及实例分析,真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性,需要的朋友可以参考下
    2023-07-07
  • Mysql中group by 使用中发现的问题

    Mysql中group by 使用中发现的问题

    当使用MySQL的GROUP BY语句时,根据指定的列对结果进行分组,这种情况通常是由于在 GROUP BY 中选择的字段与其他非聚合字段不兼容,或者在 SELECT 子句中没有正确使用聚合函数所导致的,本文给大家介绍Mysql中group by 使用中发现的问题,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • MySQL错误ERROR 2002 (HY000): Can''t connect to local MySQL server through socket

    MySQL错误ERROR 2002 (HY000): Can''t connect to local MySQL ser

    这篇文章主要介绍了MySQL错误ERROR 2002 (HY000): Can't connect to local MySQL server through socket,需要的朋友可以参考下
    2014-10-10
  • Linux下编译安装Mysql 5.5的简单步骤

    Linux下编译安装Mysql 5.5的简单步骤

    Linux下面因为从MySQL 5.5开始使用cmake来做config了,所以编译安装的会和5.1版本有些区别。不过总体来说还是差别不大
    2015-08-08
  • 在IntelliJ IDEA中使用Java连接MySQL数据库的方法详解

    在IntelliJ IDEA中使用Java连接MySQL数据库的方法详解

    这篇文章主要介绍了在IntelliJ IDEA中使用Java连接MySQL数据库的方法详解,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • MySQL三种常用存储引擎InnoDB、MyISAM、Memory深度解析

    MySQL三种常用存储引擎InnoDB、MyISAM、Memory深度解析

    在线教程千万篇,为什么你的 SQL 还是慢?因为没理解存储引擎的底层机制,本文将带你从源码角度彻底搞懂 InnoDB 行锁、聚簇索引,并给出生产环境的最优选型,需要的朋友可以参考下
    2026-05-05
  • 将SQL查询结果保存为新表的方法实例

    将SQL查询结果保存为新表的方法实例

    有时我们要把查询的结果保存到新表里,创建新表,查询,插入显得十分麻烦,下面这篇文章主要给大家介绍了关于将SQL查询结果保存为新表的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • 亲手教你怎样创建一个简单的mysql数据库

    亲手教你怎样创建一个简单的mysql数据库

    数据库是存放数据的“仓库”,维基百科对此形象地描述为“电子化文件柜”,这篇文章主要介绍了亲手教你怎样创建一个简单的mysql数据库,需要的朋友可以参考下
    2022-11-11
  • win10 安装mysql 8.0.18-winx64的步骤详解

    win10 安装mysql 8.0.18-winx64的步骤详解

    这篇文章主要介绍了win10 安装mysql 8.0.18-winx64的步骤,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-11-11

最新评论