题记

首先,祝大家新年快乐,牛年大吉,万事如意!

我们常常从关于MySQL的各种技术博客、书籍上面看到这样一句话:“不能在字段上使用函数,否则就不会走索引”。那么这句话的依据是什么呢?

本文就围绕“不能在字段上使用函数,否则就不会走索引”这句话来展开分析。

Talk is cheap. Show me the code

假设存在这样一张交易记录表-trade_log,该表中存在三个字段id(主键),operator_id(操作人id)、create_time(交易时间)。建表语句:

CREATE TABLE `trade_log` (`id` int NOT NULL AUTO_INCREMENT,`operator_id` int DEFAULT NULL,`create_time` datetime DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB ;

表中有13条记录:

INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (1, 1, '2021-02-18 09:36:22');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (2, 2, '2021-02-18 09:37:32');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (3, 3, '2021-03-04 09:37:34');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (4, 4, '2021-04-08 09:37:37');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (5, 5, '2021-05-08 09:37:40');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (6, 6, '2021-02-18 09:37:42');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (7, 7, '2021-02-18 09:37:46');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (8, 8, '2021-02-18 09:37:49');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (9, 9, '2021-02-18 09:37:53');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (10, 10, '2021-02-18 09:37:57');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (11, 11, '2021-02-18 09:37:59');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (12, 12, '2021-02-18 09:38:01');
INSERT INTO `user_database`.`trade_log`(`id`, `operator_id`, `create_time`) VALUES (13, 13, '2021-02-18 09:38:06');

现在有一个需求:统计所有二月份的交易记录总数,SQL语句是这样:

select count(*) from trade_log where MONTH(create_time) = 2;

可以看到,在create_time这个条件字段上使用了month()函数,首先我们先猜测下有没有走idx_create_time这个索引呢?

使用explain命令,查看这条SQL语句的执行结果:

mysql> EXPLAIN select count(*) from trade_log where MONTH(create_time) = 2;
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
| id | select_type | table     | partitions | type  | possible_keys | key             | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | trade_log | NULL       | index | NULL          | idx_create_time | 6       | NULL |   13 |   100.00 | Using where; Using index |
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
1 row in set (0.02 sec)

答案是不是和预想的不一样,居然走idx_create_time索引了!那么这是为什么呢?

在分析问题之前,需要先明白两个概念:索引快速定位全索引扫描

我们都知道InnoDB是索引组织表,底层数据结构为B+树,而B+树提供的索引快速定位能力是基于同一层兄弟节点有序性来实现的。

上图是idx_create_time的索引示意图,方框上的数字是month()函数计算框内日期后得到的值。正因为是B+树提供的索引快速定位能力是基于同一层兄弟节点有序性来实现的这个原因,当传入2(month(create_time) = 2)的时候,在树的第一层就不知道该怎么办了。因为只有对索引使用month()函数计算后,才知道到底等不等于2,而这就破坏了索引值的有序性,因此优化器就决定放弃走树搜索功能(索引快速定位)。

但是并不意味着优化器放弃使用这个索引,explain命令返回的Extra 字段中的Using index以及key = idx_create_time佐证了这一点。

虽然无法使用索引快速定位能力,但是优化器可以选择遍历主键索引,也可以选择遍历索引idx_create_time,但是优化器对比这两个索引大小后发现,索引idx_create_time更小,遍历这个索引树意味着比遍历主键索引树来得更快(主键索引叶子节点存放着行数据,这意味着每个数据页上能存放的索引较少),因此最终还是会选择使用索引idx_create_time。即使用全索引扫描,rows = 13证明了的确进行了全表扫描。

既然我们已经知道在字段上使用函数导致不能走索引的原因,那么我们可以在字段上进行范围查询来让优化器使用索引快速定位能力。

select count(*) from trade_log where create_time BETWEEN '2021-02-01 00:00:00' AND '2021-03-01 00:00:00'; /*如果有其它年份继续拼接**/

继续通过explain命令来查看执行结果:

mysql> EXPLAIN select count(*) from trade_log where create_time BETWEEN '2021-02-01 00:00:00' AND '2021-03-01 00:00:00';
+----+-------------+-----------+------------+-------+-----------------+-----------------+---------+------+------+----------+--------------------------+
| id | select_type | table     | partitions | type  | possible_keys   | key             | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-----------+------------+-------+-----------------+-----------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | trade_log | NULL       | range | idx_create_time | idx_create_time | 6       | NULL |   10 |   100.00 | Using where; Using index |
+----+-------------+-----------+------------+-------+-----------------+-----------------+---------+------+------+----------+--------------------------+
1 row in set (0.03 sec)

可以发现这一次的rows就等于10,意味着走了索引快速定位,而不是全索引扫描。

总结

因为在存在索引的字段上执行了函数操作(month()),而函数可能会破坏索引值的有序性(B+树提供的索引快速定位能力是基于同一层兄弟节点有序性来实现,因此导致优化器决定放弃索引快速定位能力)而转向全索引扫描。

解决上面列出的这个场景所引发的问题并不难,我们可以利用范围查询来解决。但现实中会有其它更多更杂的场景,这就要我们通过业务代码或者其它方式来避免在索引字段上进行函数操作,而是在值上进行索引操作。

MySQL-在字段上使用函数不会走索引的原因是什么?相关推荐

  1. 18 | 在索引字段上加函数踩坑经验

    一.条件字段上加函数操作 背景:现有交易记录表 tradelog 包含交易流水号(tradeid).交易员 id(operator).交易时间(t_modified)等字段: mysql> CR ...

  2. mysql数据库in函数查询是否走索引

    mysql数据库in函数查询是否走索引? IN 通常是走索引的,当IN后面的数据在数据表中超过30%的匹配时是全表的扫描,不会走索引,因此IN走不走索引与后面的数据量有关系! 昨天恰好在工作中遇到了这 ...

  3. MySQL查询为什么没走索引?这篇文章带你全面解析

    工作中,经常遇到这样的问题,我明明在MySQL表上面加了索引,为什么执行SQL查询的时候却没有用到索引? 同一条SQL有时候查询用到了索引,有时候却没用到索引,这是咋回事? 原因可能是索引失效了,失效 ...

  4. 别再问我MySQL为啥没走索引?就这几种原因,全都告诉你

    工作中,经常遇到这样的问题,我明明在MySQL表上面加了索引,为什么执行SQL查询的时候却没有用到索引? 同一条SQL有时候查询用到了索引,有时候却没用到索引,这是咋回事? 原因可能是索引失效了,失效 ...

  5. 创建了联合索引还用在单个字段上创建索引吗_数据库 索引并不是万能的

    数据库 -- 索引并不是万能的 在这里插入图片描述 索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息.如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行 ...

  6. mysql索引三个字段查询两个字段_mysql中关于关联索引的问题——对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引?...

    情况描述:在MySQL的user表中,对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引? 根据查询字段的位置不同来决定,如查询a,     a,b    a,b, ...

  7. mysql 关联索引_mysql中关于关联索引的问题——对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引?...

    情况描述:在MySQL的user表中,对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引? 根据查询字段的位置不同来决定,如查询a,     a,b    a,b, ...

  8. mysql索引三个字段查询两个字段_mysql中关于关联索引的问题——对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引?......

    情况描述:在MySQL的user表中,对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引? 根据查询字段的位置不同来决定,如查询a,     a,b    a,b, ...

  9. mysql中关于关联索引的问题——对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引?...

    情况描述:在MySQL的user表中,对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引? 根据查询字段的位置不同来决定,如查询a,     a,b    a,b, ...

  10. mysql null 走索引_mysql 索引列为Null的走不走索引及null在统计时的问题

    要尽可能地把字段定义为 NOT NULL,即使应用程序无须保存 NULL(没有值),也有许多表包含了可空列(Nullable Column) 这仅仅是因为它为默认选项.除非真的要保存 NULL,否则就 ...

最新文章

  1. Linux进程间通信六 Posix 共享内存简介与示例
  2. hive最新UDF函数(2016-10-25)
  3. 《深度学习》学习的TIP
  4. iloc,ix和loc有何不同?
  5. (转)知乎:有哪些好笑的关于程序员的笑话?
  6. 毒(得物)APP历史购买数据抓取
  7. GeoNet Deep Geodesic Networks for Point Cloud Analysis
  8. 软件渗透测试:定义、需求、过程
  9. 分享50个优秀的电子商务网站设计案例
  10. 计算机开启蓝牙网络,怎么打开电脑蓝牙功能(笔记本电脑蓝牙怎么开)
  11. LiveQing云平台直播点播流媒体服务-产品介绍及相关资源
  12. python中图形绘制技术的应用_python数据图形化—— matplotlib 基础应用
  13. 【LeetCode】词典中最长的单词(附集合操作、lamda用法)
  14. 吴恩达-DeepLearning.ai-05 序列模型(一)
  15. 基于STL的演讲比赛流程管理系统
  16. Uncaught ReferenceError: regeneratorRuntime is not defined 解决方法
  17. DRM框架(vkms)分析(4)----encoder初始化
  18. 遗忘曲线艾宾浩斯规律
  19. 中国有机淀粉市场趋势报告、技术动态创新及市场预测
  20. linux 五防实时模式与开票模式

热门文章

  1. 桌面无法显示计算机,win10 桌面计算机无法显示
  2. 自动驾驶1-5: 感知要求Requirements for Perception
  3. 阿里云云计算 40 CDN的概念
  4. 算法:匹配有效的括号20. Valid Parentheses
  5. Xcode在 release 模式下断点调试
  6. jsoup html to text,Jsoup和htmlunit结合使用。
  7. java.nio.file中的Paths类
  8. 【 Codeforces Round #551 (Div. 2) D】Serval and Rooted Tree【树形DP】
  9. 【2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) E】Cops And Roobers【最小割】
  10. 【Gym-100085 E】Eve【模拟题】