索引失效原因之——sql语句中的字段类型与数据表中的字段类型不符

一、背景

  1. 现有test_type表,表中包含1000多万行数据(12323490行),如下图所示:
    在这里插入图片描述
    表结构如下图所示,其中num字段的类型为varchar:
    test_type表,表结构
  2. 执行sql语句: select sum(money) from test_type where num = 1210;
    如下图所示:执行此sql语句花费13秒。
    在这里插入图片描述
  3. 为了减少执行此sql语句花费时间,现为数据表中num字段建立索引:
    CREATE INDEX index_num ON test_type (num);
    如图所示:num字段的索引创建成功。
    在这里插入图片描述
  4. 此时,执行sql语句select sum(money) from test_type where num = 1210;
    花费时间如下图所示:14.06秒。
    在这里插入图片描述
  5. 时间对比

未建索引和建立num字段索引后执行sql语句花费时间对比:

未建索引花费时间(s) num字段建索引后花费时间(s)
13.00 14.06

通过时间对比的表格我们可以看出,建立num字段索引后执行sql语句花费时间比未建索引花费时间增加了1.06秒。

明明建立了索引,为什么在一张表中执行同样的sql语句,花费时间反而增加了?是索引的问题吗?带着疑惑的我查看了索引使用情况:
explain select sum(money) from test_type where num = 1210\G;
如下图所示:
其中type=ALL,key=NULL, rows=11056647说明建立索引名是index_num的num字段的索引没有使用成功。
在这里插入图片描述
在num字段成功建立索引,但是执行sql语句的时候却没有用到索引,这又是为什么呢?

二、猜测

我执行的sql语句是:select sum(money) from test_type where num = 1210;其中,num字段在数据表中的类型是varchar,varchar类型在使用的时候应该加‘ ’(单引号),应该是num=‘1210’。
我的猜测:我在执行sql语句时num字段没有加单引号,被认为是int类型,但是num字段在数据表中是varchar类型,类型不符,导致索引失效。

三、实验

带着上边的猜测,我做了如下实验:

实验一

  1. 实验目的:
    验证执行sql语句时,num字段加‘ ’(单引号),能够成功使用索引。
  2. 实验过程:
    1)查看索引使用情况
    explain select sum(money) from test_type where num = ‘1210’\G;
    如下图所示:其中type=ref,key=index_num, rows=94638说明建立索引名是index_num的num字段的索引使用成功。
    在这里插入图片描述
    2)查看建立索引后,执行sql语句花费时间:
    select sum(money) from test_type where num = ‘1210’;
    如图所示:花费3.83秒。
    在这里插入图片描述
  3. 实验结果:
    执行sql语句时,num字段加‘ ’(单引号),能够成功使用索引。

现在已经证明了num字段加‘ ’(单引号)后,执行sql语句的时候能够成功使用num字段建立的索引了。
为了进一步验证本篇文章”背景“中出现的索引失效,是因为sql语句中的字段类型与数据表中的字段类型不符,我又进行了下面的实验:

实验二

  1. 实验目的:
    验证num字段在数据表中的类型(int)和执行sql语句时候num字段的类型(int)一致时,能够成功使用索引。
  2. 实验过程:
    1)查看表结构:desc test_type;
    如图所示:此时num字段在数据表中类型为int。
    在这里插入图片描述
    2)执行sql语句花费时间:
    select sum(money) from test_type where num = 1210;
    如图所示:花费8.97秒。
    在这里插入图片描述
    3)添加索引:CREATE INDEX index_num ON test_type (num);
    如图所示:num字段的索引创建成功。
    在这里插入图片描述
    4) 查看索引使用情况
    explain select sum(money) from test_type where num = 1210\G;
    如下图所示:其中type=ref,key=index_num, rows=95394说明建立索引名是index_num的num字段的索引使用成功。
    在这里插入图片描述
    5)执行sql语句花费时间:
    select sum(money) from test_type where num = 1210;
    如图所示:花费3.53秒。
    在这里插入图片描述
    6) 时间对比
    未建索引和建立num字段索引后执行sql语句花费时间对比:
未建索引花费时间(s) num字段建索引后花费时间(s)
8.97 3.53

通过时间对比的表格我们可以看出,建立num字段索引后执行sql语句花费时间比未建索引花费时间减少了5.44秒。
3. 实验结果:num字段在数据表中的类型(int)和执行sql语句时候num字段的类型(int)一致时,能够成功使用索引。

那么问题又来了,既然数据表中num的类型和执行sql语句时候的num类型不一致,为什么能够查询到数据呢?
带着疑问,我查了MySQL官方文档,找到了原因:因为根据字段在数据表中的类型,sql语句会隐式转换(下图红色框中内容)。来源:MySQL 8.0 Reference Manual
链接:https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html
在这里插入图片描述
现在已经通过实验验证了我的猜测:sql语句中的字段类型与数据表中的字段类型不符,会导致索引失效。
聪明机智的小点子又冒了出来:在sql语句里,除了给int类型加引号,能够使int类型转为varchar类型以外,有没有强制类型转换的方法呢?
带着这一疑问,我做了第三个实验。

实验三

  1. 实验目的:
    验证执行sql语句时,使用强制类型转换(把int转换成varchar),能够使索引不失效。
  2. 实验过程:
    1)查看表结构:
    如图所示:此时num字段在数据表中类型为varchar。
    在这里插入图片描述
    2)添加索引:CREATE INDEX index_num ON test_type (num);
    如图所示:num字段的索引创建成功。
    在这里插入图片描述
    3)查看强制类型转换后索引的使用情况:
    explain select sum(money) from test_type where num = CAST(1210 AS CHAR)\G;
    如下图所示:其中type=ref,key=index_num, rows=94638说明建立索引名是index_num的num字段的索引使用成功。
    在这里插入图片描述
    其中CAST函数是数据类型强制转换函数,此函数的语法规则是:cast(字段值 as 转换的类型 )
    4)执行sql语句花费时间:
    select sum(money) from test_type where num = CAST(1210 AS CHAR);
    如图所示:花费3.62秒。
    在这里插入图片描述
  3. 实验结果:执行sql语句时,使用强制类型转换(把int转换成varchar),能够使索引不失效。

四、总结

  1. sql语句中的字段类型与数据表中的字段类型不符,会导致索引失效。
  2. sql语句中的字段类型与数据表中的字段类型不符时,可以使用强制类型转换(CAST函数)转换成相符的类型(相符的类型:sql语句中的num字段是varchar类型,数据表中的num字段是varchar类型,就称sql语句中的num字段类型与数据表中的num字段类型相符)。
发布了22 篇原创文章 · 获赞 0 · 访问量 356

猜你喜欢

转载自blog.csdn.net/weixin_41796393/article/details/103833614