1

我有以下 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.col6ORDER BY.

我尝试了这个解决方案,但没有奏效。

如何使用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 _ 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.

4

5 回答 5

1

我能想到的解决这个问题的唯一另一种方法是将表更改为默认顺序 - 然后order by从您的查询中一起删除。

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

于 2012-11-22T18:05:57.193 回答
1

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

此外,索引对 MySQL 排序数据的容易程度几乎没有影响,尽管您应该检查它是否使用 BTREE 索引,因为它们更有可能保持相对良好的排序。

最糟糕的是,如果您还没有这样做的话,您可能想阅读 MySQL 手册中关于ORDER BY 优化的内容。我看不到任何适用的东西,但你可能会看到我遗漏的东西。

于 2012-11-22T17:37:50.320 回答
0

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

由于 where 子句,您正在对表 2 执行 LEFT-JOIN,但需要 Col4 或 Col5 = 'Y' 从而指示 INNER JOIN(记录必须存在于 Table1 AND Table2 上)。

同样,在您的表 3 上连接到表 1,但限制距离 <= 10,因此 LEFT-JOIN 并不准确,因为它是 WHERE 的一部分,表示 REQUIRED。

因此,下面列出了我修改后的查询。我基本上已经将“WHERE”组件移到了相应的表 JOIN 命令中。如果您确实是想拥有 LEFT-JOIN 并且想要所有表 1 记录,而不管 Table2/Table3 记录,那么您可以简单地将这些更改为 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.
于 2012-11-22T22:05:03.083 回答
0

如果您的查询与您指示的一样快,我希望它返回一个相当小的数据集。如果是这样的话,我会按原样包装你的,然后把它放在外面,比如......

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

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

希望能为您解决。

于 2012-11-22T18:57:03.773 回答
0

你为什么LIKE对非通配符值做 s ?这将使计划者更加悲观。但大多数情况下,您需要查看查询计划器显示的内容。

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

于 2012-11-23T03:56:35.363 回答