Mysql子查询总是在做filesort

时间:2015-10-09 12:48:22

标签: mysql indexing innodb

我有一张带有索引的gameplatform_pricehistory表格: (id_appcountrydateup

这样做

explain select dateup from gamesplatform_pricehistory
    where id_app=1 and country=1
    order by dateup desc limit 1

显示"使用where;使用索引"

但是有一个子查询:

explain select app.id, (select dateup from gamesplatform_pricehistory
                           where id_app=app.id and country=1
                           order by dateup desc limit 1)
      from app where id > 0;

显示使用where;使用索引;使用filesort

这是一个直接显示问题的sqlfiddle: http://sqlfiddle.com/#!2/034bc/1

基准数百万行: (table games_platform与app相同):

SELECT sql_no_cache thepricehistory.dateup
    FROM games_platform
    LEFT JOIN (SELECT max(dateup) as dateup, id_app
                   FROM gamesplatform_pricehistory
                   WHERE country='229' GROUP BY id_app
              ) thepricehistory
                         ON thepricehistory.id_app =games_platform.id
    WHERE games_platform.id=2 

eval:0.8s

SELECT sql_no_cache ( SELECT dateup FROM gamesplatform_pricehistory
                        WHERE id_app= games_platform.id AND country='229'
                        ORDER BY dateup DESC LIMIT 1
                    ) AS dateup
    FROM games_platform
    WHERE games_platform.id=2 

eval:0.0003s

2 个答案:

答案 0 :(得分:1)

Using filesort不一定是坏事。这个名字有点误导。虽然它包含" file",但这并不意味着数据写在硬盘上的任何地方。它仍然只是在内存中处理。

来自manual

  

MySQL必须执行额外的传递以找出如何按排序顺序检索行。排序是通过根据连接类型遍历所有行并将排序键和指针存储到与WHERE子句匹配的所有行的行来完成的。然后对键进行排序,并按排序顺序检索行。见第8.2.1.11, “ORDER BY Optimization”节。

您理解为什么会在您的查询中发生这种情况,对吧?使用这种子查询是一种糟糕的风格,因为它是一个依赖子查询。对于app表中的每一行,都会执行子查询。很坏。使用join重写查询。

select app.id,
gp.dateup
from app 
join gamesplatform_pricehistory gp on gp.id_app = app.id
where app.id > 0
and gp.country = 1
and gp.dateup = (SELECT MAX(dateup) FROM gamesplatform_pricehistory smgp WHERE smgp.id_app = gp.id_app AND smgp.country = 1)
;

这仍然使用从属子查询,但explain看起来好多了:

| id |        select_type | table |  type | possible_keys |     key | key_len |                        ref | rows |                    Extra |
|----|--------------------|-------|-------|---------------|---------|---------|----------------------------|------|--------------------------|
|  1 |            PRIMARY |   app | index |       PRIMARY | PRIMARY |       4 |                     (null) |    2 | Using where; Using index |
|  1 |            PRIMARY |    gp |   ref |        id_app |  id_app |       5 |    db_2_034bc.app.id,const |    1 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY |  smgp |   ref |        id_app |  id_app |       5 | db_2_034bc.gp.id_app,const |    1 |              Using index |

重写它的另一种方法是:

select app.id,
gp.dateup
from app 
LEFT join 
(SELECT id_app, MAX(dateup) AS dateup 
 FROM gamesplatform_pricehistory
 WHERE country = 1
 GROUP BY id_app
)gp on gp.id_app = app.id
where app.id > 0
;

解释看起来更好:

| id | select_type |                      table |  type | possible_keys |     key | key_len |    ref | rows |                    Extra |
|----|-------------|----------------------------|-------|---------------|---------|---------|--------|------|--------------------------|
|  1 |     PRIMARY |                        app | index |       PRIMARY | PRIMARY |       4 | (null) |    2 | Using where; Using index |
|  1 |     PRIMARY |                 <derived2> |   ALL |        (null) |  (null) |  (null) | (null) |    2 |                          |
|  2 |     DERIVED | gamesplatform_pricehistory | index |        (null) |  id_app |      13 | (null) |    2 | Using where; Using index |

这是一个根本没有依赖子查询的版本:

select app.id,
gp.dateup
from app 
left join gamesplatform_pricehistory gp on gp.id_app = app.id and country = 1
left join gamesplatform_pricehistory gp2 on gp.id_app = app.id and country = 1 and gp.dateup < gp2.dateup
where app.id > 0
and gp2.dateup is null
;

它的工作方式如下:当gp.dateup达到最大值时,没有gp2.dateup

答案 1 :(得分:0)

请提供SHOW CREATE TABLE

其中一个复合索引可能会有所帮助:

INDEX(id_app, country, dateup)
INDEX(country, id_app, dateup)