当前位置: 首页 > article >正文

MySQL中 IN 到底走不走索引?

文章目录

  • 前言
  • 数据库表结构
  • 查询sql
  • EXPLAIN介绍
    • EXPLAIN 的输出每列解释
  • 强制走索引
  • 查询时添加条件(复合索引字段)
    • 查询小时
    • 查询分钟
  • 总结


前言

在 MySQL 中,IN 语句是否能够利用索引取决于多个因素,包括但不限于查询的具体形式、表的统计信息、索引的选择度等。以下通过几个案例来帮助理解 IN 语句与索引使用的关系。

数据库表结构

CREATE TABLE `device_record_gongdi` (
  `id` varchar(32) NOT NULL COMMENT '主键',
  `device_code` varchar(32) NOT NULL COMMENT '设备code',
  `device_type` tinyint(1) NOT NULL COMMENT '设备类型(0:微站设备 1:工地扬尘监测)',
  `time_type` tinyint(1) DEFAULT NULL COMMENT '时间类型(1:一分钟 2:五分钟 3:一小时)',
  `cn` varchar(4) DEFAULT NULL COMMENT '时间类型编码: 2011 分钟、2051 5分钟、2061 小时',
  `aqi` int(11) DEFAULT NULL,
  `level` varchar(50) DEFAULT NULL COMMENT '等级',
  `pollutions` varchar(255) DEFAULT NULL COMMENT '首要污染物',
  `zhzs` double(11,3) DEFAULT NULL COMMENT '综合指数',
  `pm25_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'PM25 指定时间内平均值',
  `pm10_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'PM10 指定时间内平均值',
  `so2_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'SO2 指定时间内平均值',
  `no2_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'NO2 指定时间内平均值',
  `co_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'CO 指定时间内平均值',
  `data_time` datetime DEFAULT NULL COMMENT '数据采集时间',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `gd_data_code_type_time` (`device_code`,`time_type`,`data_time`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='工地数据表';

复合索引(联合索引):(device_code,time_type,data_time)

查询sql

使用EXPLAIN查看是否走索引

 EXPLAIN
 SELECT *
 FROM device_record_gongdi
 WHERE device_code in ('ZR802241106062','ZR802240801012');

在这里插入图片描述
可以看到没有走索引

EXPLAIN介绍

EXPLAIN 关键字在 MySQL 中是一个非常有用的工具,它可以帮助我们了解 MySQL 如何执行SQL语句。通过使用 EXPLAIN,可以获取 MySQL 执行查询的计划,这包括如何连接表、使用哪些索引、是否进行了文件排序或临时表等详细信息。

EXPLAIN 的输出每列解释

  • id: 表示查询中每个部分的选择标识符。如果查询比较简单,可能只有一个 id。对于复杂查询(如子查询),可能会有多个 id。
  • select_type: 显示对应行是简单还是复杂 SELECT 类型。例如:
    • SIMPLE: 简单查询,不包含子查询或 UNION。
    • PRIMARY: 最外层的查询。
    • SUBQUERY: 子查询中的第一个 SELECT。
    • DERIVED: 派生表(在 FROM 子句中的子查询)。
  • table: 显示该行引用的表名。
  • type: 显示连接类型,从最佳到最差依次为:system, const, eq_ref, ref, range, index, 和 ALL。理想情况下,应尽量避免 ALL 类型,因为它表示全表扫描。
  • possible_keys: 显示 MySQL 在查询过程中可以使用的索引。
  • key: 实际上被 MySQL 选择用来加速查询的索引。如果没有索引被使用,则值为 NULL。
  • key_len: 被选中索引使用的字节数,可以通过这个值来判断是否使用了复合索引的部分字段。
  • ref: 显示与索引比较的列或常量。
  • rows: 估算出找到所需的记录所需要读取的行数,值越小越好
  • Extra: 包含不适合在其他列中显示的额外信息,比如“Using where”, “Using index”, “Using temporary”, “Using filesort”等。

本文主要观察type、key和rows列。

强制走索引

通过使用force index让sql强制走索引

 EXPLAIN
 SELECT *
 FROM device_record_gongdi force index (gd_data_code_type_time)
 WHERE device_code in ('ZR802241106062','ZR802240801012');

在这里插入图片描述
可以看到强制走索引后,rows值没有减少,仍然为382258行。
继续向下看

查询时添加条件(复合索引字段)

表中time_type为时间类型,1代表一分钟数据,2代表五分钟数据,3代表一小时数据。
表中创建了复合索引:(device_code,time_type,data_time)

查询小时

 EXPLAIN
 SELECT *
 FROM device_record_gongdi
 WHERE device_code in ('ZR802241106062','ZR802240801012')
  AND time_type = 3;

在这里插入图片描述
可以看到in又走了索引,rows扫描的行数降到了3013

查询分钟

 EXPLAIN
 SELECT *
 FROM device_record_gongdi
 WHERE device_code in ('ZR802241106062','ZR802240801012')
  AND time_type = 1;
 EXPLAIN
 SELECT *
 FROM device_record_gongdi force index (gd_data_code_type_time)
 WHERE device_code in ('ZR802241106062','ZR802240801012')
  AND time_type = 1;

重点观察rows扫描的行数
在这里插入图片描述
在这里插入图片描述
这里可以发现,在执行sql时,in一般是走索引的。只有当mysql的查询优化器认为走索引时也需要扫描大量的数据时,就会变为全表扫描。(有些地方写的是当走索引查询后,rows值在数据表中的占比不超过 30% 时,大概率会走索引)

分别执行这两条sql
在这里插入图片描述
在这里插入图片描述
从这里也可以看出,当使用索引和不使用索引查询时间相差无几时,in就会不走索引。
注意:查询出的条数不同是因为查询时有数据插入

总结

MySQL 中IN语句不一定会走索引,具体取决于多种因素:

  • IN一般是走索引的。只有当mysql的查询优化器认为走索引时也需要扫描大量的数据时,就会变为全表扫描。
  • 当索引覆盖了IN查询的所有列,即查询所需的数据都能从索引中获取,MySQL 可以使用这个索引来加速查询。
  • IN值列表中的值在索引的前缀位置,MySQL 能够利用索引加速查询。若IN值列表的值不在索引的前缀位置,MySQL 无法借助索引加速查询。
  • 也可以使用EXISTS替代IN,在某些场景下能显著提升查询性能。


http://www.kler.cn/a/580587.html

相关文章:

  • GStreamer —— 2.9、Windows下Qt加载GStreamer库后运行 - “教程9:媒体信息收集“(附:完整源码)
  • 年末网络安全检查的清单
  • 力扣hot100二刷——哈希、双指针、滑动窗口
  • Python第十六课:深度学习入门 | 神经网络解密
  • PyTorch 中的混合精度训练方法,从 autocast 到 GradScalar
  • 力扣hot100_二叉树(4)_python版本
  • FastAPI 表单参数与文件上传完全指南:从基础到高级实战 [特殊字符]
  • 如何实现创意的角落切割效果:使用 CSS 和 SVG 创建时尚的网页元素
  • NLP常见任务专题介绍(1)-关系抽取(Relation Extraction, RE)任务训练模板
  • TypeError: Cannot convert object to primitive value
  • JavaScript(最后一个元素的索引就是数组的长度减 1)array.length - 1
  • Java 大视界 -- 基于 Java 的大数据实时数据处理框架性能评测与选型建议(121)
  • 请谈谈 TypeScript 中的接口(interface)和类型别名(type alias),它们的区别是什么?
  • Python | 机器学习中最常用的超参数及使用示例
  • Qt常用控件之树形QTreeWidget
  • 如何使用postman来测试接口
  • Synology 部署的 WordPress 無法升級至最新版本時,可以透過以下改良版指南進行排查和解決。
  • 雪藏HsFreezer(游戏冻结工具) v2.21
  • jsonRPC实现前后端分离
  • delphi 正则提取html中的内容