即使所有相关列都已编制索引,添加ORDER BY也会显着降低JOIN查询的速度。我怎样才能让它更快?

时间:2012-11-22 16:55:38

标签: mysql join sql-order-by query-optimization

我有以下JOIN查询:

SELECT
    table1.*, 
    table2.*
FROM 
    Table1 AS table1 
LEFT JOIN 
    Table2 AS table2 
USING 
    (col1)
LEFT JOIN 
    Table3 as table3 
USING 
    (col1) 
WHERE 
    3963.191 * 
    ACOS(
    (SIN(PI() * $usersLatitude / 180) * SIN(PI() * table3.latitude / 180)) 
    +
    (COS(PI() * $usersLatitude / 180) * COS(PI() * table3.latitude / 180) * COS(PI() * table3.longitude / 180 - PI() * 37.1092162 / 180))
    ) <= 10 
AND 
    table1.col1 != '1' 
AND 
    table1.col2 LIKE 'A' 
AND 
    (table1.col3 LIKE 'X' OR table1.col3 LIKE 'X-Y') 
AND 
    (table2.col4 = 'Y' OR table2.col5 = 'Y') 

0.15秒下执行。

但是,如果我只是添加:

ORDER BY 
    table1.col6 DESC 

执行时间超过 3秒

查询中的所有列均为已编入索引,包括table1.col6中使用的ORDER BY

我尝试了this solution,但它没有用。

如何使用ORDER BY快速运行此查询。


修改

结果EXPLAIN EXTENDED 没有 ORDER BY

id  select_type table   type    possible_keys   key key_len ref rows    filtered    Extra
1   SIMPLE  table1  ALL PRIMARY,col2,col3   NULL    NULL    NULL    140101  72.61   Using where
1   SIMPLE  table2  eq_ref  PRIMARY,col4,col5   PRIMARY 4   table1.col1 1   100 Using where
1   SIMPLE  table3  eq_ref  PRIMARY PRIMARY 4   table1.col1 1   100 Using where

EXPLAIN EXTENDED WITH ORDER BY的结果:

id  select_type table   type    possible_keys   key key_len ref rows    filtered    Extra
1   SIMPLE  table1  ALL PRIMARY,col2,col3   NULL    NULL    NULL    140101  72.61   Using where; Using filesort
1   SIMPLE  table2  eq_ref  PRIMARY,col4,col5   PRIMARY 4   table1.col1 1   100 Using where
1   SIMPLE  table3  eq_ref  PRIMARY PRIMARY 4   table1.col1 1   100 Using where

编辑2:

查询中所有列的数据类型(根据要求):

col1: int(11)
col2: char(1)
col3: varchar(3)
col4: char(1)
col5: char(1)
col6: int(11)
latitude: varchar(25)
longitude: varchar(25)

所有3个表(table1,table2和table3)都是MyISAM

5 个答案:

答案 0 :(得分:1)

这不是一个解决方案,但它可能有所帮助:避免使用'*'指定所需的列并明确指定它们。当你使用*来指定所有列时,MySQL必须做更多的工作,在数据排序时分流数据,因此更有可能做一个文件输出,这很慢。

此外,索引对MySQL可以轻松订购数据的影响很小或没有影响,但您应该检查它是否使用BTREE索引,因为它们更有可能保持相对良好的秩序。

最糟糕的是,如果您还没有这样做,可能需要阅读MySQL手册对ORDER BY optimisation的评论。我看不到任何适用的东西,但你可能会看到我遗漏的东西。

答案 1 :(得分:1)

我能想到解决此问题的另一种方法是将表更改为默认订单 - 然后从查询中删除order by

http://dev.mysql.com/doc/refman/5.1/en/alter-table.html

答案 2 :(得分:0)

如果您的查询速度与您指示的一样快,我预计它会返回一个相当小的数据集。如果是这种情况,我会按原样包装你的,然后将ORDER BY OUTIDE放在... ...

select
      PreQuery.*
   from
      ( Your Entire Query Without the order by clause ) as PreQuery
   order by
      PreQuery.Col6 Desc

这样,外部查询与现有索引无关,只返回数据...然后该结果将按原始结果应用顺序。

希望能为你解决问题。

答案 3 :(得分:0)

除了我的其他答案提供将现有查询包装在select中之外,然后对结果进行排序。这是查询的另一个选项......如果我的解释不正确,请纠正我。

你正在对表2进行LEFT-JOIN,但由于where子句,要求Col4或Col5 ='Y',因此表示INNER JOIN(表1和表2中必须存在记录)。

类似地,你的表3加入表1,但是限制了距离&lt; = 10,所以LEFT-JOIN并不真正准确,因为它是WHERE的一部分,表示需要。

所以,我修改过的查询如下所示。我基本上将“WHERE”组件移动到相应的表JOIN命令。如果你真的DID意味着拥有LEFT-JOIN并希望所有表1记录表2 /表3的记录,那么你可以简单地将它们改为LEFT JOIN。

SELECT STRAIGHT_JOIN
      T1.*, 
      T2.*
   FROM 
      Table1 AS T1
         JOIN Table2 AS T2
            ON T1.Col1 = T2.Col1
            AND ( T2.Col4 = 'Y' OR T2.Col5 = 'Y' )
         JOIN Table3 as T3
            ON T1.Col1 = T3.Col1
            AND 3963.191 
               * ACOS(  (SIN(PI() * $usersLatitude / 180) * SIN(PI() * T3.latitude / 180)) 
                                + (  COS(PI() * $usersLatitude / 180) * COS(PI() * T3.latitude / 180) 
                                   * COS(PI() * table3.longitude / 180 - PI() * 37.1092162 / 180)
                        )   
                     ) <= 10 
   WHERE
          T1.Col2 LIKE 'A'
      AND ( T1.col3 LIKE 'X' OR T1.col3 LIKE 'X-Y') 
      AND T1.Col1 != '1'
   ORDER BY
      T1.Col6

如果直接应用order by子句,则将第6列添加到表1的索引

On Table 1, I would have an index on ( Col2, Col3, Col1, Col6 )
On Table 2 on ( Col1, Col4, Col5 )
On Table 3 on ( Col1 )   <-- assume already had this.

答案 4 :(得分:0)

为什么在非通配符值上执行LIKE?这将使计划者变得更加悲观。但大多数情况下,您需要查看查询计划程序显示的内容。

另外,您最近在桌子上做过ANALYZE吗?如果行统计信息已关闭,则规划人员将做出糟糕的选择。