子查询
当一个查询是另外一个查询的条件时,此时称之为子查询;
子查询又分为独立子查询和相关子查询,顾名思义,独立子查询表示该子查询可以独立完成,不依赖于外部变量;而相关子查询则依赖于外部数据传入;
慢查询日志
Mysql可以通过修改/etc/my.cnf来设定慢查询日志。修改my.cnf文件:
slow_query_log = 1 slow_query_log_file = /tmp/mysqlslow.log long_query_time = 0.0001 log-queries-not-using-indexes log-slow-admin-statements log-slow-slave-statements
然后重启mysql
#>service mysqld restart我们设定的超时时间是0.0001秒,当查询超过这个时间就会打印慢查询日志。
实例
查询一张表,查询出每月最后一天的数据并展示;
我们可能第一时间写出的sql类似于这样:
SELECT * FROM orders where date in (select max(date) from orders group by substring(orders.date, -10, 7));
注意,这个sql语句用到了in,按照我们自然的想法,我们会认为这个sql将会先执行in后面的子查询,再执行外部查询。但实际上是这样吗?
我们用explain解释一下这个sql,返回:
原来,在mysql执行这个sql语句时,该语句被翻译为下面的语句执行:
通过慢查询日志我们也可以看到该子查询的逻辑查询次数:
我们用explain解释一下这个sql,返回:
mysql> explain SELECT * FROM orders where date in (select max(date) from orders group by substring(orders.date, -10, 7)); +----+--------------------+----------+------+---------------+------+---------+------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+----------+------+---------------+------+---------+------+------+---------------------------------+ | 1 | PRIMARY | orders | ALL | NULL | NULL | NULL | NULL | 93 | Using where | | 2 | DEPENDENT SUBQUERY | orders | ALL | NULL | NULL | NULL | NULL | 93 | Using temporary; Using filesort | +----+--------------------+----------+------+---------------+------+---------+------+------+---------------------------------+我们看到后面的子查询是一个DEPENDENT SUBQUERY,也就是一个相关子查询。
原来,在mysql执行这个sql语句时,该语句被翻译为下面的语句执行:
SELECT * FROM orders where date exists (select max(date) from orders group by substring(orders.date, -10, 7) having `max(date)`=date);解释之后是作为一个相关子查询来执行,假设外部查询的时间复杂度是O(M), 相关子查询的时间复杂度是O(N), 则此查询的时间复杂度不是O(M+N)而是O(M+M*N)
通过慢查询日志我们也可以看到该子查询的逻辑查询次数:
# Time: 140701 15:28:05 # User@Host: root[root] @ localhost [] # Query_time: 0.015520 Lock_time: 0.000162 Rows_sent: 4 Rows_examined: 9480 SET timestamp=1404199685; SELECT * FROM orders where date in (select max(date) from orders group by substring(orders.date, -10, 7));我们当前的表行数只有93行,但是这个sql执行时逻辑查询次数竟然达到了9480次。因此in的效率很低,最好尽量避免使用in语句。
优化
可以通过降低相关子查询表的条数来降低整体逻辑查询次数,例如这个sql可以优化为:SELECT * FROM orders A where exists (select * from (select max(date) from orders group by substring(orders.date, -10, 7)) B where B.`max(date)`=A.date);解释这个sql可以看到:
+----+--------------------+------------+------+---------------+------+---------+------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+------------+------+---------------+------+---------+------+------+---------------------------------+ | 1 | PRIMARY | A | ALL | NULL | NULL | NULL | NULL | 93 | Using where | | 2 | DEPENDENT SUBQUERY |相关子查询的行数只有4行了。大大降低了逻辑查询次数,从慢查询日志也反映了这个改变:| ALL | NULL | NULL | NULL | NULL | 4 | Using where | | 3 | DERIVED | youku_tj | ALL | NULL | NULL | NULL | NULL | 93 | Using temporary; Using filesort | +----+--------------------+------------+------+---------------+------+---------+------+------+---------------------------------+
# Time: 140701 15:27:27 # User@Host: root[root] @ localhost [] # Query_time: 0.000915 Lock_time: 0.000201 Rows_sent: 4 Rows_examined: 560 SET timestamp=1404199647; SELECT * FROM orders A where exists (select * from (select max(date) from orders group by substring(orders.date, -10, 7)) B where B.`max(date)`=A.date);因此当通过explain查看sql的执行效率时,要注意DEPENDENT SUBQUERY对效率的影响,从两个SQL的执行效率看,改进后的执行之间是0.00915s,改进之前是0.015520s 还是影响比较大的,尤其对行数很大的表影响尤其明显;
没有评论:
发表评论