最近发现服务器里mysql对CPU的占用明显提高了,昨天晚上把慢查询日志打开,今天过来看到了一个反复出现的慢查询,贴上原SQL:

SELECTc.id,c.vin,c.license_plate,c.owner_name,c.model,c.file_id,c.path,count( w.id ) AS count,count( IF ( w.type = 1, TRUE, NULL ) ) AS type_1,count( IF ( w.type = 2, TRUE, NULL ) ) AS type_2,count( IF ( w.type = 3, TRUE, NULL ) ) AS type_3,count( IF ( w.type = 4, TRUE, NULL ) ) AS type_4,count( IF ( w.type = 5, TRUE, NULL ) ) AS type_5,count( IF ( w.type = 6, TRUE, NULL ) ) AS type_6,count( IF ( w.type = 7, TRUE, NULL ) ) AS type_7,count( IF ( w.type = 8, TRUE, NULL ) ) AS type_8,count( IF ( w.type = 9, TRUE, NULL ) ) AS type_9,count( IF ( w.type = 10, TRUE, NULL ) ) AS type_10,count( IF ( w.type = 11, TRUE, NULL ) ) AS type_11,count( IF ( w.type = 12, TRUE, NULL ) ) AS type_12,count( IF ( w.type = 13, TRUE, NULL ) ) AS type_13,count( IF ( w.type = 14, TRUE, NULL ) ) AS type_14,count( IF ( w.type = 15, TRUE, NULL ) ) AS type_15,count( IF ( w.type = 16, TRUE, NULL ) ) AS type_16,count( IF ( w.type = 17, TRUE, NULL ) ) AS type_17,count( IF ( w.type = 18, TRUE, NULL ) ) AS type_18,count( IF ( w.type = 19, TRUE, NULL ) ) AS type_19,count( IF ( w.type = 20, TRUE, NULL ) ) AS type_20,count( IF ( w.type = 21, TRUE, NULL ) ) AS type_21
FROMcar AS cLEFT JOIN warning_message AS w ON w.car_id = c.id AND w.create_time BETWEEN '2021-08-26 00:00:00' AND '2021-08-26 23:59:59'
WHEREc.path LIKE '/1/%' AND c.deleted = 0
GROUP BYc.id,c.vin,c.license_plate,c.owner_name,c.model,c.path,c.file_id
ORDER BYcount DESC,c.create_time DESC LIMIT 0,20;

这段查询主要是展示一段时间内车辆报警汇总并根据报警总数进行排序,查询时长1.2秒。

最终我们目的是获得一个这样的信息:

车辆信息1 车辆信息2 报警总数 第一种报警的数量 第二种报警的数量
粤A12345 本田 99 98 1
沪A12345 丰田 32 2 30

这里面涉及两个表,一个是车辆信息表,一个是根据车辆ID关联到的报警明细表,逻辑很简单,两个表关联后,将需要展示的车辆信息 group by 后得到以车辆为维度的车辆信息,并且使用一系列的 count(IF) 计算出每一种类型的报警数量,并且计算一个 count(报警ID) 作为报警总数,然后对报警总数和车辆的创建时间进行倒序以获得最终的数据。

显然,问题主要是出在了那一系列的 group by 和 对报警总数的排序 这两个点上,让我们逐个分析解决。

首先是那一系列的group by,实际上我们只需要 c.id 这个聚组就足够达到按车聚组的目的,其它的聚组只是为了这个字段可以在select中使用,我们完全可以通过再联一次car表的形式,将其它的聚组字段去掉:

SELECTc.id,
-- 这部分的SQL从 c表 换成了 c1表,c1表在下面left joinc1.vin,c1.license_plate,c1.owner_name,c1.model,c1.file_id,c1.path,count( w.id ) AS count,count( IF ( w.type = 1, TRUE, NULL ) ) AS type_1,count( IF ( w.type = 2, TRUE, NULL ) ) AS type_2,count( IF ( w.type = 3, TRUE, NULL ) ) AS type_3,count( IF ( w.type = 4, TRUE, NULL ) ) AS type_4,count( IF ( w.type = 5, TRUE, NULL ) ) AS type_5,count( IF ( w.type = 6, TRUE, NULL ) ) AS type_6,count( IF ( w.type = 7, TRUE, NULL ) ) AS type_7,count( IF ( w.type = 8, TRUE, NULL ) ) AS type_8,count( IF ( w.type = 9, TRUE, NULL ) ) AS type_9,count( IF ( w.type = 10, TRUE, NULL ) ) AS type_10,count( IF ( w.type = 11, TRUE, NULL ) ) AS type_11,count( IF ( w.type = 12, TRUE, NULL ) ) AS type_12,count( IF ( w.type = 13, TRUE, NULL ) ) AS type_13,count( IF ( w.type = 14, TRUE, NULL ) ) AS type_14,count( IF ( w.type = 15, TRUE, NULL ) ) AS type_15,count( IF ( w.type = 16, TRUE, NULL ) ) AS type_16,count( IF ( w.type = 17, TRUE, NULL ) ) AS type_17,count( IF ( w.type = 18, TRUE, NULL ) ) AS type_18,count( IF ( w.type = 19, TRUE, NULL ) ) AS type_19,count( IF ( w.type = 20, TRUE, NULL ) ) AS type_20,count( IF ( w.type = 21, TRUE, NULL ) ) AS type_21
FROMcar AS cLEFT JOIN warning_message AS w ON w.car_id = c.id AND w.create_time BETWEEN '2021-08-26 00:00:00' AND '2021-08-26 23:59:59'
-- 这里再left join一个c1表进来LEFT JOIN car AS c1 ON c1.id = c.id
WHEREc.path LIKE '/1/%' AND c.deleted = 0
GROUP BYc.id
-- 聚组这里就可以删掉原本那一大串的字段,只留一个 c.id
ORDER BYcount DESC,c.create_time DESC LIMIT 0,20;

这样我们就只 group by c.id 了

接下来,我们看看排序的问题,显然这里使用c.create_time的排序,完全可以换成c.id的排序,因为c.id是自增主键,自增本身就代表了时间,做以下修改:

-- 原本的排序
ORDER BYcount DESC,c.create_time DESC
-- 改成
ORDER BYcount DESC,c.id DESC 

最大的问题是,这里需要根据一个 count 出来的字段排序,这个排序会引起扫全表,逻辑很简单,只有扫了全表计算出来所有车辆的报警总数,才有办法进行排序

我们可以分成两步执行,先通过一个查询获取车辆报警的20条(根据原limit来),获取到20个car_id再在 where 里使用c.id in (car id list) 的形式查询到这部分的排序:

SELECTcar_id,count(*) AS warning_count
FROMwarning_message AS w
LEFT JOIN car AS c ON c.id = w.car_id
WHEREw.create_time BETWEEN '2021-08-26 00:00:00' AND '2021-08-26 23:59:59' -- 数据车辆筛选的一些条件在这里面进行筛选,以得出最终所需的ID
GROUP BYcar_id
ORDER BYwarning_count DESC LIMIT 0,20;

这段SQL执行只需要0.01秒,在代码里执行这段sql,取到20个需要的car_id,最终添加到原本的where里:

SELECTc.id,c1.vin,c1.license_plate,c1.owner_name,c1.model,c1.file_id,c1.path,count( w.id ) AS count,count( IF ( w.type = 1, TRUE, NULL ) ) AS type_1,count( IF ( w.type = 2, TRUE, NULL ) ) AS type_2,count( IF ( w.type = 3, TRUE, NULL ) ) AS type_3,count( IF ( w.type = 4, TRUE, NULL ) ) AS type_4,count( IF ( w.type = 5, TRUE, NULL ) ) AS type_5,count( IF ( w.type = 6, TRUE, NULL ) ) AS type_6,count( IF ( w.type = 7, TRUE, NULL ) ) AS type_7,count( IF ( w.type = 8, TRUE, NULL ) ) AS type_8,count( IF ( w.type = 9, TRUE, NULL ) ) AS type_9,count( IF ( w.type = 10, TRUE, NULL ) ) AS type_10,count( IF ( w.type = 11, TRUE, NULL ) ) AS type_11,count( IF ( w.type = 12, TRUE, NULL ) ) AS type_12,count( IF ( w.type = 13, TRUE, NULL ) ) AS type_13,count( IF ( w.type = 14, TRUE, NULL ) ) AS type_14,count( IF ( w.type = 15, TRUE, NULL ) ) AS type_15,count( IF ( w.type = 16, TRUE, NULL ) ) AS type_16,count( IF ( w.type = 17, TRUE, NULL ) ) AS type_17,count( IF ( w.type = 18, TRUE, NULL ) ) AS type_18,count( IF ( w.type = 19, TRUE, NULL ) ) AS type_19,count( IF ( w.type = 20, TRUE, NULL ) ) AS type_20,count( IF ( w.type = 21, TRUE, NULL ) ) AS type_21
FROMcar AS cLEFT JOIN warning_message AS w ON w.car_id = c.id AND w.create_time BETWEEN '2021-06-29 00:00:00' AND '2021-08-27 23:59:59'LEFT JOIN car AS c1 ON c1.id = c.id
WHEREc.path LIKE '/1/%' AND c.deleted = 0 AND c.id IN (166963, 167547, 256976, 166964, 167523,168357, 167000, 168018, 167992, 256727,257354, 257150, 166820, 168373, 167866,168765, 167065, 168093, 257297,257298)
GROUP BYc.id
ORDER BYcount desc,c.id DESC LIMIT 0,20

执行结果显示,最终的时间是0.06秒,加上获取车辆ID列表的SQL,最终我们以不到0.1秒的时间完成原本1.2秒的工作。附上前后Explain:

优化前:

优化后:

有句话是这么说的:添加索引优化99%,其他方式优化1%。这类型的SQL优化,我们最终要做的就是让查询尽可能是用到索引,除掉没必要的查询,使用其他成本更低的查询来代替成本高的查询,并且要善于分析使用场景,合理添加联合索引,能把效率提高不少。

mysql多表关联 group by + order by 优化相关推荐

  1. mysql join 组合索引_详解MySQL两表关联的连接表创建单列索引还是组合索引最优...

    概述 今天主要介绍一下MySQL中两表关联的连接表是如何创建索引的相关内容,下面来看看详细的介绍. MySQL两表关联的连接表创建索引 创建数据库的索引,可以选择单列索引,也可以选择创建组合索引. 假 ...

  2. MySQL多表关联查询效率高点还是多次单表查询效率高,为什么?

    这里写目录标题 MySQL多表关联查询对比多次单表查询,哪个效率高? 疑问: 高手解答: <阿里巴巴JAVA开发手册>里面写超过三张表禁止join 这是为什么?这样的话那sql要怎么写? ...

  3. mysql 子表 关联查询语句_MySQL-基本查询语句及方法,连表和子查询

    一.基本查询语句 create table emp( id intnotnull unique auto_increment, name varchar(20) notnull, sex enum(' ...

  4. mysql 单表关联_MySQL 基础之 单表、多表联查

    使用和不使用not null 的区别: 不使用: 查询时用'name is null' 作为条件 mysql>create table t8( -> id int auto_increme ...

  5. mysql delete 表关联删除数据_mysql delete 多表连接删除功能

    单个表的删除: DELETE FROM tableName WHERE columnName = value; 删除表内的所有行: 即:保留表的结构.属性.索引 DELETE FROM tablena ...

  6. kettle使用--1.mysql多表关联导入mongoDB

    文章目录 1. 初步体验:csv 转为excel Kettle概念 配置mysql链接 mysql 一对多关联查询结果保存到mongodb中 1. 初步体验:csv 转为excel Windows环境 ...

  7. MySQL多表关联数据同时删除sql语句

    DELETE删除多表数据,怎样才能同时删除多个关联表的数据呢?这里做了深入的解释: 代码如下 复制代码 1 delete from t1 where 条件 2 delete t1 from t1 wh ...

  8. Mysql多表关联查询

    ​​​​​三表(replay_case.replay_task及general_flow)联合查询,注意select时,如果某个字段在不同的表中有重复的话需要指定具体的表,比如应该使用:select ...

  9. mysql多表关联update

    日常的开发中一般都是写的单表update语句,很少写多表关联的update. 在MySQL中,update的多表连接更新和select的多表连接查询在使用的方法上存在一些小差异. 来看一个具体的例子. ...

最新文章

  1. python Unicode转ascii码的一种方法
  2. 代理(Proxy)模式
  3. docker 添加端口映射_Docker容器修改端口映射
  4. input.get_shape()的用法
  5. CF835E-The penguin‘s game【交互】
  6. 4路视频+4路百电(物理隔离)+8路电话+开关量+串口+电话光端机 武警光端机
  7. 引用自己创建的css样式表_如何使用CSS创建联系表
  8. 2.4一元多项式的表示及相加,含cpp算法
  9. javascript / jquery 操作 cookie
  10. Cisco VTP protocol   实验笔记
  11. MapReduce运行时出现java.lang.NoClassDefFoundError
  12. 二:C#对象、集合、DataTable与Json内容互转示例;
  13. 直流无刷电机与永磁同步电机区别
  14. 【抽象代数概念速查】Lagrange Interpolation-拉格朗日插值
  15. [Java并发]の其二
  16. 在这里,看到未来——2013微软技术节印象
  17. Cesium Primitives加载大量图标点
  18. 第一章 Arm 架构科普解读(2023新)
  19. c#网页设计 UI —登录注册界面
  20. 跟我一起做微信开发(一)——开通微信公共号(开发模式)

热门文章

  1. 前端ajax跨域处理
  2. 谈谈抖音号的购买市场为何如此大!
  3. 前端工具:好用的配色网站推荐
  4. python函数def里面嵌套def_python 函数嵌套函数_Python中的嵌套函数
  5. MySql数据库导出表结构
  6. 拼图游戏--C++语言实现
  7. Mac系统上老碰到cannot be opened because the developer cannot be verified的问题解决方案
  8. java 周几_java根据年月日判断周几
  9. 亚马逊云科技基于智能搜索,为企业打造知识库
  10. 面向对象的概念与3大特征