MySQL虚拟列提高检索效率

介绍

MySQL 5.7 开始支持了一个新特性 虚拟列(Generated columns , 又称生成列 / 计算列) ,该列的值是通过在列定义时包含的一个计算表达式得到的。

作用: 当我们在where语句中对表内列进行计算时,会导致索引失效而降低查询效率,这种情况可以通过创建虚拟列,提前对字段进行计算,在查询时候直接通过虚拟列筛选即可。

使用场景:

  • 需要对json列进行操作:

    mysql 复制代码
    -- 对JSON列进行操作时,无法走索引,此时可以通过创建虚拟列直接计算结果,提高筛选效率。
    select * from table
    where 
    JSON_CONTAINS(t1.server_types,JSON_OBJECT('serverType', 1)) 
    or 
    JSON_CONTAINS(t1.server_types,JSON_OBJECT('serverType', 2)) 
  • 排序时通过多个字段计算:

    mysql 复制代码
    -- 可以建立虚拟列直接存储计算后的结果,然后再建立索引 提高order by效率。
    select * from table
    order by has_resource desc, (visit_score * 0.5 + follow_score * 0.3 + app_score * 0.2 + add_score) desc

语法及注意事项

建立虚拟列语法

mysql 复制代码
-- GENERATED ALWAYS可以省略
-- VIRTUAL或 STORED表示虚拟列的存储方式。
ALTER TABLE 表名称 add column 虚拟列名称 虚拟列类型 [GENERATED ALWAYS] as (表达式) [VIRTUAL | STORED];

-- 示例:
-- 通过birthday计算年龄
ALTER TABLE test add column age int as (DATE_FORMAT(FROM_DAYS(TO_DAYS(NOW())-TO_DAYS(birthday)), '%Y')+0 STORED);

虚拟列存储方式

  • VIRTUAL(默认):不存储列值,在读取表的时候自动计算并返回,不消耗任何存储,这种存储方式仅 InnoDB 支持设置索引。
    • 不会占用额外的物理存储空间来存储计算结果,每次查询时根据定义的表达式动态生成值。
    • 提高查询效率的原因在于它可以简化查询语句和查询过程。例如,假设你经常需要在一个复杂表达式的结果上进行过滤或排序,而这个表达式是基于表中其他字段计算得出的。通过创建一个虚拟列,MySQL可以在内部自动处理这个计算,避免在每个查询中重复执行相同的计算逻辑,特别是在JOIN、GROUP BY或者ORDER BY等操作中使用到该计算结果时,可以减少查询的CPU消耗。
  • STORED:在插入或更新时计算存储列值,存储的虚拟列需要存储空间,并且 MyISAM 也可以设置索引。
    • 直接存储在硬盘,检索效率更高。
    • 增删改时维护,会降低DML语句性能。

虚拟列的注意事项

VIRTUAL 和 STORED类型如何选择:

  1. 如果表不经常修改,则建议创建 STORED类型
  2. 如果表经常修改,并且计算逻辑不复杂,则建议建立VIRTUAL索引,因为VIRTUAL类型不占用磁盘空间,在执行增删改语句时不会影响性能。
  3. 如果计算逻辑复杂,建议创建STORED类型。

虚拟列允许

  • 允许在一个表中混合使用虚拟列和存储列
  • 允许按虚拟列分区
  • 虚拟列定义可以引用其他虚拟列,但只能引用表中定义较早出现的列。
  • 虚拟列允许修改表达式,但不允许修改存储方式(只能通过删除重新创建来修改)。
  • 可以将 STORED 虚拟列与普通列可以互相转化,但 VIRTUAL 不行。

虚拟列不允许

  • 不允许存储函数可加载函数
  • 不允许存储过程或函数参数
  • 不允许使用变量(系统变量、用户定义变量、局部变量、sysdate()、UUID()等函数)
  • 不允许子查询(只能操作当前表内的字段)
  • 虚拟列定义不允许使用自增 (AUTO_INCREMENT),也不允许使用自增基列
  • 外键约束不能引用虚拟列
  • 触发器不能使用或引用虚拟列
  • 被虚拟列引用的基列不允许使用外键约束、AS 或引用操作。

使用案例

对JSON列进行筛选,性能低时

mysql 复制代码
create table test(
    json_str json
);
-- 假设表内有很多数据
insert into test values
('[{"type":"name","value":"张三"},{"type":"job","value":"JAVA"}]'),
('[{"type":"name","value":"李四"},{"type":"job","value":"GO"}]'),
('[{"type":"name","value":"王五"},{"type":"job","value":"Python"}]'),
('[{"type":"name","value":"老六"},{"type":"job","value":"PHP"}]');

-- 筛选出 job=java的数据,查询速度很慢
select * from test
where
JSON_EXTRACT(json_str, '$[1].value') = 'JAVA';

-- 创建虚拟列,预先存储job值
alter table test add job varchar(20) as (REPLACE(JSON_EXTRACT(json_str, '$[1].value'),'"',''))

-- 直接筛选虚拟列
select * from test where job = 'JAVA'
相关推荐
爱学习的阿磊1 分钟前
使用Fabric自动化你的部署流程
jvm·数据库·python
枷锁—sha7 分钟前
【SRC】SQL注入快速判定与应对策略(一)
网络·数据库·sql·安全·网络安全·系统安全
惜分飞19 分钟前
ORA-600 kcratr_nab_less_than_odr和ORA-600 4193故障处理--惜分飞
数据库·oracle
chian-ocean20 分钟前
CANN 生态进阶:利用 `profiling-tools` 优化模型性能
数据库·mysql
m0_5500246323 分钟前
持续集成/持续部署(CI/CD) for Python
jvm·数据库·python
AC赳赳老秦24 分钟前
代码生成超越 GPT-4:DeepSeek-V4 编程任务实战与 2026 开发者效率提升指南
数据库·数据仓库·人工智能·科技·rabbitmq·memcache·deepseek
啦啦啦_999938 分钟前
Redis-2-queryFormat()方法
数据库·redis·缓存
玄同7651 小时前
SQLite + LLM:大模型应用落地的轻量级数据存储方案
jvm·数据库·人工智能·python·语言模型·sqlite·知识图谱
吾日三省吾码1 小时前
别只会“加索引”了!这 3 个 PostgreSQL 反常识优化,能把性能和成本一起打下来
数据库·postgresql
chian-ocean2 小时前
百万级图文检索实战:`ops-transformer` + 向量数据库构建语义搜索引擎
数据库·搜索引擎·transformer