MySQL中的函数索引(Generated Column)及一次SQL优化

MySQL 中是没有 Oracle 的函数索引功能的,把 MySQL 的 Generated Column 称为“函数索引”并不准确,但可以和函数索引达到同样的效果,也有人把这个特性称为“衍生列”。

Generated Column 是什么

Generated Column 的值是根据其定义的表达式所计算而来的,下面使用官方文档中的例子做个简单介绍。

有一张表存储直角三角形的三条边长,大家都知道,根据直角三角形的边长公式,斜边的长度可以通过另外两条边长计算得到,这样就可以在表中只存储两条直角边,而斜边通过 Generated Column 定义,创建这张表并插入一条数据:

CREATE TABLE triangle (
  sidea DOUBLE,
  sideb DOUBLE,
  sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb))
);
INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);

sidea 和 sideb 是两条直角边,sidec 是斜边,insert 时只需要插入两条直角边,也就是说 Generated Column 不能人为操作(插入、更新、删除),会自动根据其定义表达式计算得到。

查询这张表:

mysql> SELECT * FROM triangle;
+-------+-------+--------------------+
| sidea | sideb | sidec              |
+-------+-------+--------------------+
|     1 |     1 | 1.4142135623730951 |
|     3 |     4 |                  5 |
|     6 |     8 |                 10 |
+-------+-------+--------------------+

Generated Column 定义语法

Generated Column 的定义语法如下:

col_name data_type [GENERATED ALWAYS] AS (expr)
  [VIRTUAL | STORED] [NOT NULL | NULL]
  [UNIQUE [KEY]] [[PRIMARY] KEY]
  [COMMENT 'string']

关键字“AS”指明了这个字段是衍生的,是 Generated Column,AS 后面就是用以计算的表达式。GENERATED ALWAYS 使定义更明确,可以省略。

VIRTUAL 和 STORED 是 Generated Column 的两种类型,指明该字段的值如何存储:

  • VIRTUAL: Virtual Generated Column 的值不会持久化到磁盘,只保存在数据字典中(表的元数据),每次读取时在 BEFORE 触发器后就会立即计算。
  • STORED:Stored Generated Column 的值会持久化到磁盘上,而不是每次读取时计算。

如果不指明的话,MySQL 会默认以 VIRTUAL 的形式实现,STORED 需要更多的磁盘空间,性能也没有明显的优势,所以一般使用 VIRTUAL。

Generated Column 定义要点

  • 一般情况下,Generated Column 可以使用内置函数及操作符定义。如果给定相同的数据,多次调用会产生相同的结果,这样的定义是明确被允许的。否则,定义会失败,例如使用 NOW()CURRENT_USER()CONNECTION_ID()的定义会失败。
  • 自定义的函数和存储过程,不允许使用。
  • 变量,例如系统变量、自定义变量等不允许使用。
  • 子查询不允许使用。
  • Generated Column 的定义中可以依赖其他 Generated Column 字段,但所依赖的衍生字段必须定义在它的前面。如果只依赖非衍生字段,则定义顺序没有要求。
  • 自增长 AUTO_INCREMENT 不允许使用。
  • 自增长的列,不能用到 Generated Column 的定义中。
  • 从 MySQL 5.7.10 开始,如果表达式计算导致截断或给函数提供了不正确的输入,则create table语句将终止,并返回DDL操作。

一次SQL优化

通过慢查询日志找到一条慢SQL,执行计划如下:

mysql> EXPLAIN
SELECT
    c.id,
    b.customer_status
FROM
    t_core_customer c
    INNER JOIN t_core_customer_bizinfo b ON c.id = b.customer_id AND b.biz_id = 'maintain'
WHERE
    REPLACE ( REPLACE ( c.customer_name, '(', '(' ), ')', ')' ) = '天津买斯扣科技有限公司';
+----+-------------+-------+------------+--------+----------------------------------+---------+---------+--------------------------------+---------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys                    | key     | key_len | ref                            | rows    | filtered | Extra       |
+----+-------------+-------+------------+--------+----------------------------------+---------+---------+--------------------------------+---------+----------+-------------+
|  1 | SIMPLE      | b     | NULL       | ALL    | idx_core_customer_bizinfo_cidbid | NULL    | NULL    | NULL                           | 1263918 |    10.00 | Using where |
|  1 | SIMPLE      | c     | NULL       | eq_ref | PRIMARY                          | PRIMARY | 110     | b.customer_id                  |       1 |   100.00 | Using where |
+----+-------------+-------+------------+--------+----------------------------------+---------+---------+--------------------------------+---------+----------+-------------+
2 rows in set (0.05 sec)

客户表中有117万行数据,这条SQL执行耗时4秒多,通过执行计划可以看到,客户表没有走索引而进行全表扫描,customer_name 字段的索引由于 replace 函数没有被利用到。

增加 Generated Column :

ALTER TABLE `t_core_customer`
ADD COLUMN `customer_name_replaced` varchar(200)  AS (REPLACE(REPLACE(customer_name, '(', '(' ), ')', ')' )); 

创建索引:

ALTER TABLE `t_core_customer`
ADD INDEX `customer_name_replaced`(`customer_name_replaced`) USING BTREE;

优化后再看执行计划:

mysql> EXPLAIN
SELECT
    c.id,
    b.customer_status
FROM
    t_core_customer c
    INNER JOIN t_core_customer_bizinfo b ON c.id = b.customer_id AND b.biz_id = 'maintain'
WHERE
    REPLACE ( REPLACE ( c.customer_name, '(', '(' ), ')', ')' ) = '天津买斯扣科技有限公司';
+----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+-----------------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys                    | key                              | key_len | ref                         | rows | filtered | Extra |
+----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+-----------------------------+------+----------+-------+
|  1 | SIMPLE      | c     | NULL       | ref  | PRIMARY,customer_name_replaced   | customer_name_replaced           | 603     | const                       |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | b     | NULL       | ref  | idx_core_customer_bizinfo_cidbid | idx_core_customer_bizinfo_cidbid | 222     | c.id,const                  |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+-----------------------------+------+----------+-------+
2 rows in set (0.40 sec)

执行计划正常,利用了索引,SQL耗时到了10毫秒以内。

原文地址:https://www.cnblogs.com/cellei/p/12443294.html

时间: 2024-07-28 17:52:02

MySQL中的函数索引(Generated Column)及一次SQL优化的相关文章

Mysql中的函数

阅读目录 什么是函数 与存储过程的区别 mysql自带函数 自定义函数 什么是函数 mysql中的函数与存储过程类似,都是一组SQL集: 与存储过程的区别 函数可以return值,存储过程不能直接return,但是有输出参数可以输出多个返回值: 函数可以嵌入到sql语句中使用,而存储过程不能: 函数一般用于实现较简单的有针对性的功能(如求绝对值.返回当前时间等),存储过程用于实现复杂的功能(如复杂的业务逻辑功能): mysql自带函数 mysql本身已经实现了一些常见的函数,如数学函数.字符串函

MySQL中concat函数(连接字符串)

MySQL中concat函数使用方法:CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. 注意:如果所有参数均为非二进制字符串,则结果为非二进制字符串. 如果自变量中含有任一二进制字符串,则结果为一个二进制字符串.一个数字参数被转化为与之相等的二进制字符串格式:若要避免这种情况,可使用显式类型 cast, 例如:SELECT CONCAT(CAST(int_col AS CHAR), char_col) MySQL的con

mysql中能够使用索引的典型场景

mysql 演示数据库:http://downloads.mysql.com/docs/sakila-db.zip 匹配全值 explain select * from rental where rental_date='2005-05-25 17:22:10' and inventory_id=373 and customer_id=343 匹配值的范围查询 explain select * from rental where customer_id >= 373 and customer_i

高性能MySQL中的三星索引

高性能MySQL中的三星索引 我对此提出了深深的疑问: 一星:相关的记录指的是什么??(相关这个词很深奥,“相关部门”是什么部门) 二星:如果建立了B-Tree(B+Tree)索引,数据就有序了.三星:索引的列包含了查询需要所有的列?根本不需要在where查询条件所有的列上建立索引! 我认为一星和二星的rows应该是columns,索引不关具体的数据行,只与查询的列有关.这样也与High Performance MySQL 后面提到的多列索引的观点相符合,特别是二星评估. 个人的观点: 评估一个

MySQL中group_concat函数 --- 很有用的一个用来查询出所有group by 分组后所有 同组内的 内容

本文通过实例介绍了MySQL中的group_concat函数的使用方法,比如select group_concat(name) . MySQL中group_concat函数 完整的语法如下: group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符']) 基本查询 mysql> select * from aa; +------+------+ | id| name | +------+------+ |1 |

MySQL中ROW_NUMBER()函数的替换实现

SELECT t.*, @RowNum := @RowNum + 1 AS RowNum FROM t, (SELECT @RowNum := 0) AS myRows MySQL中没有ROW_NUMBER()函数,可以用以上代码替换. MySQL中ROW_NUMBER()函数的替换实现

MySQL中concat函数

使用方法:CONCAT(str1,str2,-) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. 注意:如果所有参数均为非二进制字符串,则结果为非二进制字符串. 如果自变量中含有任一二进制字符串,则结果为一个二进制字符串.一个数字参数被转化为与之相等的二进制字符串格式:若要避免这种情况,可使用显式类型 cast, 例如:SELECT CONCAT(CAST(int_col AS CHAR), char_col) MySQL的concat函数可以连接一个或者多

MySQL中group_concat函数-和group by配合使用

MySQL中group_concat函数 完整的语法如下: group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符']) 基本查询 Sql代码 收藏代码 select * from aa; +------+------+ | id| name | +------+------+ |1 | 10| |1 | 20| |1 | 20| |2 | 20| |3 | 200 | |3 | 500 | +------+

mysql中concat函数的使用相关总结

concat(str1,str2) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. mysql> select concat('11','22','33'); +------------------------+ | concat('11','22','33') | +------------------------+ | 112233 | +------------------------+ 1 row in set (0.00 sec) MySQL的co