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'
相关推荐
0xDevNull4 小时前
MySQL数据冷热分离详解
后端·mysql
科技小花4 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸4 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain4 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希5 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神5 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员5 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java5 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿5 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴6 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存