MySQL in查询优化

Posted qwangxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL in查询优化相关的知识,希望对你有一定的参考价值。

https://blog.csdn.net/gua___gua/article/details/47401621

 

 

mysql in查询优化<一>

原创 2015年08月10日 17:57:56
  • 5137

开发说他写了个SQL特别慢,让看看。

[html] view plain copy
 
  1. select * from t_channel where id_ in(select distinct cdbh from sjkk_gcjl where jgsj>‘2015-01-02 08:00:00‘ and jgsj<‘2015-01-02 12:00:00‘);  
  2. ......  
  3. 30min+  

 

然后我查询内部SQL,只需要3s+

 

[html] view plain copy
 
  1. mysql> select distinct cdbh from sjkk_gcjl where jgsj>‘2015-01-02 08:00:00‘ and jgsj<‘2015-01-02 12:00:00‘;  
  2. .....  
  3. 1755 rows in set (3.30 sec)  
  4.   
  5. mysql> select count(*) from t_channel;  
  6. ....  
  7. 12062 rows in set (0.70 sec)  
开发写的SQL为啥那么慢呢?看看执行计划

 

 

[html] view plain copy
 
  1. explain extended select * from t_channel where id_ in(select distinct cdbh from sjkk_gcjl where jgsj>‘2015-01-02 08:00:00‘ and jgsj<‘2015-01-02 12:00:00‘);  
  2. +----+--------------------+-----------+-------+---------------+----------+---------+------+--------+----------+------------------------------+  
  3. | id | select_type        | table     | type  | possible_keys | key      | key_len | ref  | rows   | filtered | Extra                        |  
  4. +----+--------------------+-----------+-------+---------------+----------+---------+------+--------+----------+------------------------------+  
  5. |  1 | PRIMARY            | t_channel | ALL   | NULL          | NULL     | NULL    | NULL |  12062 |   100.00 | Using where                  |  
  6. |  2 | DEPENDENT SUBQUERY | sjkk_gcjl | range | idx_jgsj      | idx_jgsj | 8       | NULL | 731868 |   100.00 | Using where; Using temporary |  
  7. +----+--------------------+-----------+-------+---------------+----------+---------+------+--------+----------+------------------------------  
看看数据库转换后的语句
[html] view plain copy
 
  1. mysql> show warnings;  
  2. SELECT  
  3.     `shanghai_full`.`t_channel`.`ID_` AS `ID_`,  
  4.     `shanghai_full`.`t_channel`.`Code_` AS `Code_`,  
  5.      ...... --这里会列出所有字段  
  6. FROM  
  7.     `shanghai_full`.`t_channel`  
  8. WHERE  
  9.     in_optimizer > (  
  10.         `shanghai_full`.`t_channel`.`ID_` ,EXISTS > (  
  11.             SELECT DISTINCT  
  12.                 1  
  13.             FROM  
  14.                 `shanghai_full`.`sjkk_gcjl`  
  15.             WHERE  
  16.                 (  
  17.                     (  
  18.                         `shanghai_full`.`sjkk_gcjl`.`jgsj` > ‘2015-01-02 08:00:00‘  
  19.                     )  
  20.                     AND (  
  21.                         `shanghai_full`.`sjkk_gcjl`.`jgsj` < ‘2015-01-02 12:00:00‘  
  22.                     )  
  23.                     AND (  
  24.                         CACHE > (  
  25.                             `shanghai_full`.`t_channel`.`ID_`  
  26.                         ) = `shanghai_full`.`sjkk_gcjl`.`cdbh`  
  27.                     )  
  28.                 )  
  29.         )  
  30.     );  

可见,经过mysql优化器后,in 给转换成exists的方式(mysql认为换成exists更快,呵呵)。

慢的原因:走了exists,查询把外表t_channel做为主表进行全表扫描,每扫描一行数据,然后对子查询进行查询看这条数据是否符合条件。

特别说明:mysql版本是5.5。在5.6中mysql做了改进,将in的这种查询转换为了join。

 

优化方式一(将in查询转换为连接查询)

 

[html] view plain copy
 
  1. select t1.* from t_channel t1,(select distinct cdbh from sjkk_gcjl where jgsj>‘2015-01-02 08:00:00‘ and jgsj<‘2015-01-02 12:00:00‘) t2 where t1.id_=t2.cdbh;  
  2. ......  
  3. 1264 rows in set (3.30 sec)  
  4.   
  5. mysql> explain extended select t1.* from t_channel t1,(select distinct cdbh from sjkk_gcjl where jgsj>‘2015-01-02 08:00:00‘ and jgsj<‘2015-01-02 12:00:00‘) t2 where t1.id_www.255055.cn/ =t2.cdbh;  
  6. +----+-------------+------------+--------+---------------+----------+---------+---------+--------+----------+------------------------------+  
  7. | id | select_type | table      | type   | possible_keys | key      | key_len | ref     | rows   | filtered | Extra                        |  
  8. +----+-------------+------------+--------+---------------+----------+---------+---------+--------+----------+------------------------------+  
  9. |  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL     | NULL    | NULL    |   1755 |   100.00 |                              |  
  10. |  1 | PRIMARY     | t1    www.boshenyl.cn      | eq_ref | PRIMARY,ID_   | PRIMARY  | 74      | t2.cdbh |      1 |   100.00 | Using where                  |  
  11. |  2 | DERIVED     | sjkk_gcjl  | range  | idx_jgsj      | idx_jgsj | 8       | NULL    | 731868 |   100.00 | Using where; Using temporary |  
  12. +----+-------------+------------+--------+---------------+----------+---------+---------+--------+----------+------------------------------+  
  13.   
  14. mysql> show warnings;  
  15.   
  16. SELECT  
  17.     `shanghai_full`.`t1`.`ID_` AS `ID_`,  
  18.     ........ ---这里会列出所有字段FROM  
  19.     `shanghai_full`.`t_channel` `t1`  
  20. JOIN (  
  21.     SELECT DISTINCT  
  22.         `shanghai_full`.`sjkk_gcjl`.`cdbh` AS `www.cnzhaotai.com cdbh`  
  23.     FROM  
  24.         `shanghai_full`.www.fengshen157.com `sjkk_gcjl`  
  25.     WHERE  
  26.         (  
  27.             (  
  28.                 `shanghai_full`.`sjkk_gcjl`.`jgsj` > ‘2015-01-02 08:00:00‘  
  29.             )  
  30.             AND (  
  31.                 `shanghai_full`.`sjkk_gcjl`.`jgsj` < ‘2015-01-02 12:00:00‘  
  32.             )  
  33.         )  
  34. ) `t2`  
  35. WHERE  
  36.     (  
  37.         `shanghai_full`.`t1`.`ID_` = www.taohuayuan178.com `t2`.`cdbh`  
  38.     );  

 

优化方式二(使用memory引擎的临时表)

 

[html] view plain copy
 
  1. mysql> create temporary table tmp_www.yibaoyule1.com channel  engine=memory (select distinct cdbh from sjkk_gcjl where jgsj>‘2015-01-02 08:00:00‘ and jgsj<‘2015-01-02 12:00:00‘);  
  2. Query OK, 1755 rows affected (9.00 sec)  
  3. Records: 1755  Duplicates: 0  Warnings: 0  
  4. mysql> select * from t_channel where id_ in(select * from tmp_channel);  
  5. .....  
  6. 1264 rows in set (0.26 sec)  
  7.   
  8. mysql> explain extended select * from t_channel where id_ in(select * from tmp_channel);  
  9. +----+--------------------+-------------+------+---------------+------+---------+------+------+----------+-------------+  
  10. | id | select_type        | table       | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |  
  11. +----+--------------------+-------------+------+---------------+------+---------+------+------+----------+-------------+  
  12. |  1 | PRIMARY            | t_channel   | ALL  | NULL          | NULL | NULL    | NULL | 3224 |   100.00 | Using where |  
  13. |  2 | DEPENDENT SUBQUERY | tmp_channel | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | Using where |  
  14. +----+--------------------+-------------+------+---------------+------+---------+------+------+----------+-------------+  
  15.   
  16. mysql> show warnings;  
  17. | Note  | 1003 | select `vmc_jiaqi`.`t_channel`.`ID_` AS `ID_`,`vmc_jiaqi`.`t_channel`.`Code_` AS `Code_`,`vmc_jiaqi`.`t_channel`.`HostId_` AS `HostId_`,`vmc_jiaqi`.`t_channel`.`RoadMonitorStationId_` AS `RoadMonitorStationId_`,`vmc_jiaqi`.`t_channel`.`ChannelNo_` AS `ChannelNo_`,`vmc_jiaqi`.`t_channel`.`Name_` AS `Name_`,`vmc_jiaqi`.`t_channel`.`ChannelType_` AS `ChannelType_`,`vmc_jiaqi`.`t_channel`.`Ext_` AS `Ext_`,`vmc_jiaqi`.`t_channel`.`DeviceAddress_` AS `DeviceAddress_`,`vmc_jiaqi`.`t_channel`.`MinSpeed_`www.mhylpt.comS `MinSpeed_`,`vmc_jiaqi`.`t_channel`.`MaxSpeed_` AS `MaxSpeed_`,`vmc_jiaqi`.`t_channel`.`LimitMinRatio_` AS `LimitMinRatio_`,`vmc_jiaqi`.`t_channel`.`LimitMaxRatio_` AS `LimitMaxRatio_`,`vmc_jiaqi`.`t_channel`.`Direction_` AS `Direction_`,`vmc_jiaqi`.`t_channel`.`JcDirection_` AS `JcDirection_`,`vmc_jiaqi`.`t_channel`.`LaneNum_` AS `LaneNum_`,`vmc_jiaqi`.`t_channel`.`FrameDropNum_` AS `FrameDropNum_`,`vmc_jiaqi`.`t_channel`.`RecogizeRectLeft_` AS `RecogizeRectLeft_`,`vmc_jiaqi`.`t_channel`.`RecogizeRectTop_` AS `RecogizeRectTop_`,`vmc_jiaqi`.`t_channel`.`RecogizeRectWidth_` AS `RecogizeRectWidth_`,`vmc_jiaqi`.`t_channel`.`RecogizeRectHeight_` AS `RecogizeRectHeight_`,`vmc_jiaqi`.`t_channel`.`RmpServerIp_` AS `RmpServerIp_`,`vmc_jiaqi`.`t_channel`.`RmpServerPort_` AS `RmpServerPort_`,`vmc_jiaqi`.`t_channel`.`VirtualGateServerId_` AS `VirtualGateServerId_`,`vmc_jiaqi`.`t_channel`.`LaneType_` AS `LaneType_`,`vmc_jiaqi`.`t_channel`.`DeviceType_` AS `DeviceType_`,`vmc_jiaqi`.`t_channel`.`ManufacturerId_` AS `ManufacturerId_`,`vmc_jiaqi`.`t_channel`.`IsLocalSavePicture_` AS `IsLocalSavePicture_`,`vmc_jiaqi`.`t_channel`.`OrderNO_` AS `OrderNO_`,`vmc_jiaqi`.`t_channel`.`channelStatus` AS `channelStatus` from `vmc_jiaqi`.`t_channel` where <in_optimizer>(`vmc_jiaqi`.`t_channel`.`ID_`,<exists>(select 1 from `vmc_jiaqi`.`tmp_channel` where (<cache>(`vmc_jiaqi`.`t_channel`.`ID_`) = `vmc_jiaqi`.`tmp_channel`.`cdbh`)))   
注意:第二种方式还是使用了exists的执行方式,所以这种方式没有第一种方式好,在特定的条件下可能会有用处,

以上是关于MySQL in查询优化的主要内容,如果未能解决你的问题,请参考以下文章

带有 IN 子句的 SQL 查询的 MYSQL 查询优化

MySQL 子查询优化 - where not in(子查询)

如何优化 MySQL 中的 IN 子查询?

MySQL 查询优化:IN() 与 OR

MySQL - IN(...)优化问题中的“选择”查询(=>分层查询)

MySQL in查询优化