mysql in 改为 join_MySQL优化 exists/in改写join

问题语句

SELECT * FROMaWHERE `type` = 'appointment'

AND `event` = 14

AND EXISTS(SELECT * FROMbWHERE a.`sheet_id` =b.`id`AND `company_id` = 8

AND b.`deleted_at` IS NULL)ORDER BY a.id DESCLIMIT6;

解读执行计划

在exists类型的子查询的执行计划中,select_type一栏分别是PRIMARY和DEPENDENT SUBQUERY

4122481

DEPENDENT SUBQUERY的意思是:子查询,依赖于外层的查询;DEPEND SUBQUERY是依赖于SQL的主体部分,它的执行次数最大可能和SQL主体部分结果的行数一样多(这里因为有limit6,所以看起来主表扫描行数是6,如果去掉这个limit6的话,这个值是1500W)

上面这句话解释得通俗一点就是外连接先执行查询,然后把查询的结果集放入子查询内进行匹配;外查询每执行一次查询,就要来子查询匹配一次

join的执行计划中,select_type一栏都是simple

4122481

join的第一行的就是外表

从上面的对比可以看出,无论是 exists类型的子查询还是join,都基本可以看作遵循了第一行就是驱动表的规则(注意不是所有子查询都遵循这个规则,本篇只针对exists类型的dependent subquery)

分析

图一PRIMARY对应的表就是图二中的a表,DEPENDENT SUBQUERY表就是图二中的b表;a表有1500W行数据,b表有2W行数据

所以图一的SQL执行效率如此低下的原因就是大表驱动小表

优化

exists改写为join

1 SELECT a.*

2 FROM a join b on a.`sheet_id` =b.`id`3 WHERE a.`type` = 'appointment'

4 AND a.`event` = 14

5 AND b.`company_id` = 8

6 AND b.`deleted_at` IS NULL

7 ORDER BY a.`id` DESC

8 LIMIT 6;

由于a表作为内表,因此在a.`sheet_id`,a.`type`,a.`event`上创建联合索引;语句中出现了b表的本地谓词,所以b表的b.`company_id`,b.`deleted_at`上也要创建联合索引

优化结果,执行时间:117s→0.36s,性能提升了2000倍

这个语句有一个更极端的取值,在b.`company_id` = 2的时候,小表不会搜出任何满足条件的结果,在这种情况下,原语句执行时间在350s以上,而新语句仅需要0.03s,性能提升万倍

优化案例

今天优化的这批语句中,大多数是exists子查询的问题,可以看出这个研发小哥非常的喜欢用exists这种写法;前面的那个exists语句是泛用型,后面的exists语句加了些新花样

eg.

1 SELECT SUM(`xxxx`) ASag2 FROMa3 WHERE EXISTS(4 SELECT * FROMb5 WHERE a.`delivery_sheet_id` =b.`id`6 AND (`status` = 4

7 OR `is_rejected` = '1')8 AND `company_id` = 8

9 AND b.`deleted_at` IS NULL

10 )11 AND `status` IN (0, 4)12 AND `collection_type` IN (2, 3)13 AND a.`deleted_at` IS NULL;

or的优化通常改写union,但这里是求sum不能这么改,需要改写成2个语句然后求和;对应的列要建好索引

1 select c.ag+d.ag as ag from

2 (SELECT SUM(a.`xxxx`) ASag3 FROM a joinb4 on a.`delivery_sheet_id` =b.`id`5 where

6 b.`status` = 4

7 AND b.`company_id` = 8

8 AND b.`deleted_at` IS NULL

9 AND a.`status` IN (0, 4)10 AND a.`collection_type` IN (2, 3)11 AND a.`deleted_at` IS NULL) c,12 (13 SELECT SUM(a.`xxxx`) ASag14 FROM a joinb15 on a.`delivery_sheet_id` =b.`id`16 where

17 b.`is_rejected` = '1'

18 AND b.`company_id` = 8

19 AND b.`deleted_at` IS NULL

20 AND a.`status` IN (0, 4)21 AND a.`collection_type` IN (2, 3)22 AND a.`deleted_at` IS NULL) d;

优化结果,执行时间:18s→0.2s

in改写join的思路和exists差不多

这里没有现成的例子,粘贴一篇郑松华老师公众号的分析过来

原语句

1 SELECT

2

3 COUNT( * ) AStotalNum,4

5 sum( CASE WHEN F.ALARM_LEVEL = 1 THEN 1 ELSE 0 END ) ASLEVELS1,6

7 sum( CASE WHEN F.ALARM_LEVEL = 2 THEN 1 ELSE 0 END ) ASLEVELS2,8

9 sum( CASE WHEN F.ALARM_LEVEL = 3 THEN 1 ELSE 0 END ) ASLEVELS3,10

11 sum( CASE WHEN F.DEAL_STATE = 0 THEN 1 ELSE 0 END ) ASDESTS12

13 FROM

14

15 F16

17 LEFT JOIN DC ON DC.ID =F.CONST_ID18

19 LEFT JOIN V ON V.ID =F.VEHICLE_ID20

21 LEFT JOIN AREA ON AREA.ID =V.SYS_DIVISION_ID22

23 WHERE

24

25 DC.ID IS NOT NULL

26

27 AND V.ID IS NOT NULL

28

29 AND F.DEAL_STATE = 0

30

31 AND ALARM_LEVEL IN ( 1, 2, 3)32

33 AND F.VEHICLE_ID IN(34

35 SELECT

36

37 VEHICLE_ID38

39 FROM

40

41 GVLK42

43 WHERE

44

45 GROUP_ID IN ( SELECT GROUP_ID FROM GULK WHERE USER_ID = 'ff8080816091b09c0161f9b825750a9a')46

47 UNION

48

49 SELECT

50

51 VEHICLE_ID52

53 FROM

54

55 UVLK56

57 WHERE

58

59 USER_ID = 'ff8080816091b09c0161f9b825750a9a'

60

61 )62

63 AND date( F.ALARM_TIME ) BETWEEN '2000-01-01'

64

65 AND '2018-08-14'

66

67 AND AREA.PATH LIKE CONCAT( ( SELECT ARE.PATH FROM ARE WHERE ARE.ID = '0' ), '%' )

执行计划如下

4122481

改写如下(in改join)

1 explain extended2

3 SELECT

4

5 COUNT( * ) AStotalNum,6

7 sum( CASE WHEN F.ALARM_LEVEL = 1 THEN 1 ELSE 0 END ) ASLEVELS1,8

9 sum( CASE WHEN F.ALARM_LEVEL = 2 THEN 1 ELSE 0 END ) ASLEVELS2,10

11 sum( CASE WHEN F.ALARM_LEVEL = 3 THEN 1 ELSE 0 END ) ASLEVELS3,12

13 sum( CASE WHEN F.DEAL_STATE = 0 THEN 1 ELSE 0 END ) ASDESTS14

15 FROM

16

17 F18

19 straight_join (20

21 SELECT

22

23 VEHICLE_ID24

25 FROM

26

27 GVLK28

29 WHERE

30

31 GROUP_ID IN ( SELECT GROUP_ID FROM GULK WHERE USER_ID = 'ff8080816091b09c0161f9b825750a9a')32

33 UNION

34

35 SELECT

36

37 VEHICLE_ID38

39 FROM

40

41 UVLK42

43 WHERE

44

45 USER_ID = 'ff8080816091b09c0161f9b825750a9a'

46

47 ) s on F.VEHICLE_ID =s.VEHICLE_ID48

49 straight_join DC ON DC.ID =F.CONST_ID50

51 straight_join V ON V.ID =F.VEHICLE_ID52

53 straight_join AREA ON AREA.ID =V.SYS_DIVISION_ID54

55 WHERE

56

57 DC.ID IS NOT NULL

58

59 AND V.ID IS NOT NULL

60

61 AND F.DEAL_STATE = 0

62

63 AND ALARM_LEVEL IN ( 1, 2, 3)64

65 AND date( F.ALARM_TIME ) BETWEEN '2000-01-01'

66

67 AND '2018-08-14'

68

69 AND AREA.PATH LIKE CONCAT( ( SELECT ARE.PATH FROM ARE WHERE ARE.ID = '0' ), '%' )

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MySQL是一个开源的关系型数据库管理系统,常用于网站开发和数据存储。在使用MySQL时,经常需要进行查询操作,而IN语句是一种常见的查询优化方式。 IN语句可以在一个查询中指定多个条件,查询出满足条件的数据。然而,当IN语句中的条件过多时,可能会导致查询性能下降。为了优化这种情况,可以考虑以下几点: 1. 使用索引:为IN语句中的条件字段创建索引可以提高查询性能。索引可以帮助数据库快速定位到满足条件的数据,减少查询的时间复杂度。 2. 分批次查询:如果IN语句中的条件过多,可以考虑将条件拆分为多个小的IN查询,然后使用UNION操作将结果合并。这样可以减少单个查询的数据量,提高查询效率。 3. 使用临时表:将IN语句中的条件存储到一个临时表中,然后通过JOIN操作将临时表与需要查询的表关联起来。这样可以减少IN操作的数据量,提高查询速度。 4. 考虑使用EXISTS:如果IN语句中的条件是一个子查询,可以尝试使用EXISTS语句代替。EXISTS语句只需要判断是否存在满足条件的数据,不需要返回具体的数据,可以提高查询效率。 5. 避免使用大型的IN语句:如果IN语句中的条件非常多,可以考虑使用其他查询方式代替,例如使用JOIN操作或者使用临时表进行查询。 总之,当MySQL中的IN语句使用过多时,可以通过创建索引、使用临时表、分批次查询等方式进行优化,提高查询性能。同时,根据具体的场景和需求,可以选择合适的查询方式来替代IN语句,达到更好的查询效果。 ### 回答2: MySQL的IN子句在查询中经常被用到,它能够在一个查询中匹配多个值。然而,IN子句过多的优化是一个常见的问题。 首先,IN子句会导致MySQL执行多个子查询,这样会增加查询的执行时间。当IN列表中的值非常多时,查询的效率会进一步降低。因此,为了优化这个问题,可以尝试以下几点: 1. 使用JOIN替代IN子句:如果可能的话,尽量使用JOIN来关联表格,而不是使用IN子句。JOIN可以在一个查询中同时匹配多个值,效率比IN子句高。 2. 使用EXISTS查询EXISTS查询比IN子查询更高效。可以将IN子查询改写EXISTS查询,以提高查询效率。 3. 确保表上有适当的索引:为涉及到IN子句的列创建索引可以有效地提高查询性能。例如,如果IN子句是针对某个字段的,为该字段创建索引可以减少查询时间。 4. 分批次处理:如果IN列表中的值非常多,可以将查询多个小的批次进行处理。这样可以避免一次性查询过多的数据,提高查询效率。 5. 使用临时表:将IN子句的值存储在一个临时表中,然后将其与查询中的其他表格进行关联,可以提高查询性能。 总之,为了优化MySQL中的IN子句,我们可以尝试使用JOIN替代IN,使用EXISTS查询,增加适当的索引,分批次处理查询,或者使用临时表等方法。通过合理的优化,可以提高查询效率,减少数据库的负载。 ### 回答3: MySQL的IN语句在查询优化中是一个常见的问题。IN语句可以用来查询某一列是否在一个列表中,但是当列表的长度过长时,查询的性能可能会受到影响。 为了优化MySQL中的IN语句,可以考虑以下几个方面: 1. 使用索引:在需要进行IN语句查询的列上创建索引,可以大大提高查询性能。索引可以帮助MySQL更快地定位到需要查询的数据。 2. 使用JOIN 替代IN : 如果IN语句中的列表来自于另一个表,可以考虑使用JOIN操作来代替IN检索。JOIN操作可以将两个表连接在一起,通过连接条件将需要查询的数据找出来,这样可以减少IN查询的复杂度。 3. 利用子查询:对于IN语句中的列表,可以将其拆分为多个子查询,每个子查询处理部分数据。这样可以减小单个IN查询的数据量,从而提高查询性能。 4. 使用EXISTS : IN语句可以使用EXISTS操作来代替。EXISTS只需要判断是否存在满足条件的记录,而不需要返回具体的数据。这样可以减少查询的数据量,提高查询性能。 5. 合理使用缓存:对于频繁使用的IN查询,可以考虑将查询结果缓存在内存中,这样可以减少查询的时间开销。 总的来说,优化MySQL中的IN语句可以使用索引、JOIN操作、子查询EXISTS操作、缓存等方法来提高查询性能,具体的优化方法需结合具体的业务场景和数据量来决定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值