如何在JOIN查询中更快地进行ORDER BY?我没有尝试过任何工作

时间:2012-11-26 17:14: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') 


// Data Types of all columns in the query:
// col1: int(11)
// col2: char(1)
// col3: varchar(3)
// col4: char(1)
// col5: char(1)
// col6: int(11)
// latitude: varchar(25)
// longitude: varchar(25)

// All 3 tables (table1, table2, and table3) are `MyISAM`.

0.15秒下执行。

但是,如果我只是添加:

ORDER BY 
    table1.col6 DESC 

执行时间超过 3秒

查询中的所有列均为已编入索引,包括table1.col6中使用的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

奇怪的是,我在此网站上的其他几个查询中使用ORDER BY DESC,并且它不会在与此特定查询一样多的地方减慢速度。此查询特定的某些内容会导致ORDER BY显着减慢。

我还在所有3个表上都做了ANALYZE TABLE,他们都报告了OK。然后,我使用LIKE替换查询中的每个=,它实际上使查询没有ORDER BY 0.2秒变为 3秒。换句话说,将LIKE替换为=会使原始查询的与添加ORDER BY一样长!考虑到LIKE=做更多的工作,这怎么可能呢?也许这就是为什么ORDER BY需要这么长时间的线索?

这里我做了多少(非常成功)

1)我尝试SELECT table1.*, table2.*而不是SELECT table1.col1,而仍然接管了 3秒来完成。

2)我尝试在col1的{​​{1}},col2col3col6添加复合索引,但它没有提高执行速度。

3)我尝试this solution将查询作为子查询,然后将Table1包装在最后,但它没有提高执行速度。

4)我尝试了以下版本的查询,但它没有改进任何内容,实际上使查询接管 3秒甚至没有添加ORDER BY(也许这提供了另一条线索):

ORDER BY

...

如何使用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 // With the following composite indexes: // On Table 1, index on ( Col2, Col3, Col1, Col6 ) // On Table 2, index on ( Col1, Col4, Col5 ) // Remember, all individual columns are already indexed. 快速运行这个顽固的查询?或者这是不可能的?


修改

所有3个表的ORDER BY结果:

SHOW CREATE TABLE

编辑2:

以下是我的 MySQL配置文件。除其他事项外,请注意CREATE TABLE `Table1` ( `col1` int(11) unsigned NOT NULL AUTO_INCREMENT, `col100` varchar(25) CHARACTER SET utf8 DEFAULT NULL, `col101` varchar(60) COLLATE utf8_bin DEFAULT NULL, `col102` varchar(50) CHARACTER SET utf8 DEFAULT NULL, `col103` varchar(10) COLLATE utf8_bin DEFAULT '00000000', `col104` date NOT NULL, `col105` int(3) DEFAULT NULL, `col106` varchar(25) COLLATE utf8_bin DEFAULT NULL, `col107` varchar(20) COLLATE utf8_bin DEFAULT 'Blah', `col108` varchar(2) COLLATE utf8_bin DEFAULT 'No', `col109` varchar(15) COLLATE utf8_bin DEFAULT 'Blah', `col2` enum('A','B') COLLATE utf8_bin DEFAULT NULL, `col3` enum('A','B','A-B') COLLATE utf8_bin DEFAULT NULL, `col110` decimal(10,7) NOT NULL DEFAULT '0.0000000', `col111` decimal(10,7) NOT NULL DEFAULT '0.0000000', `col112` char(1) COLLATE utf8_bin DEFAULT 'N', `col113` char(1) COLLATE utf8_bin DEFAULT 'N', `col114` int(11) DEFAULT NULL, `col115` varchar(15) COLLATE utf8_bin DEFAULT 'Blah', `col6` int(11) DEFAULT NULL, `col117` varchar(45) COLLATE utf8_bin DEFAULT NULL, `col118` varchar(2) COLLATE utf8_bin NOT NULL, `col119` tinyint(2) NOT NULL, `col120` int(6) NOT NULL, `col121` varchar(7) COLLATE utf8_bin NOT NULL, `col122` varchar(6) COLLATE utf8_bin NOT NULL, `col123` char(1) COLLATE utf8_bin NOT NULL DEFAULT 'A', `col124` varchar(200) COLLATE utf8_bin NOT NULL, `col125` tinyint(4) NOT NULL, `col126` tinyint(1) NOT NULL, `col127` varchar(1) COLLATE utf8_bin NOT NULL DEFAULT 'A', `col128` tinyint(1) NOT NULL DEFAULT '0', `col129` smallint(5) unsigned NOT NULL, `col130` varchar(1) COLLATE utf8_bin NOT NULL DEFAULT 'A', `col131` int(11) NOT NULL, `col132` tinyint(1) NOT NULL, `col133` tinyint(1) NOT NULL, `col134` varchar(1) COLLATE utf8_bin NOT NULL, `col135` varchar(200) COLLATE utf8_bin NOT NULL, `col136` int(11) NOT NULL, `col137` int(10) unsigned NOT NULL, `col138` int(11) NOT NULL, `col139` tinyint(1) NOT NULL, `col140` tinyint(1) NOT NULL, `col141` tinyint(4) NOT NULL, `col142` varchar(25) COLLATE utf8_bin NOT NULL, `col143` varchar(25) COLLATE utf8_bin NOT NULL, `col144` tinyint(1) unsigned NOT NULL, `col145` tinyint(4) NOT NULL, PRIMARY KEY (`col1`), KEY `col2` (`col2`), KEY `col3` (`col3`), KEY `CompositeIndex0` (`col1`,`col2`,`col3`,`col6`), KEY `CompositeIndex1` (`col2`,`col3`,`col1`,`col6`), KEY `idx01` (`col1`,`col2`,`col3`) [19 other indexes that do not involve col1, col2, col3, or col6...] ) ENGINE=MyISAM AUTO_INCREMENT=160640 DEFAULT CHARSET=utf8 COLLATE=utf8_bin //*******************************************************// CREATE TABLE `Table2` ( `col1` int(11) unsigned NOT NULL DEFAULT '0', `col201` varchar(45) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT 'Blah', `col202` varchar(45) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT 'Blah', `col203` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col204` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col205` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col206` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col207` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col208` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col209` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col210` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col211` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col212` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col213` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col214` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col215` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col216` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col217` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col218` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col219` varchar(45) COLLATE utf8_bin DEFAULT 'Blah', `col220` varchar(255) COLLATE utf8_bin DEFAULT 'Blah', `col221` varchar(255) COLLATE utf8_bin DEFAULT 'Blah', `col222` varchar(255) COLLATE utf8_bin DEFAULT 'Blah', `col223` varchar(255) COLLATE utf8_bin DEFAULT 'Blah', `col224` varchar(45) COLLATE utf8_bin DEFAULT ‘Blah’, `col225` varchar(255) COLLATE utf8_bin DEFAULT NULL, `col4` char(1) COLLATE utf8_bin DEFAULT 'A', `col226` char(1) COLLATE utf8_bin DEFAULT 'A', `col227` varchar(5) COLLATE utf8_bin DEFAULT 'Blah', `col228` char(1) COLLATE utf8_bin NOT NULL, `col229` text COLLATE utf8_bin, `col5` char(1) COLLATE utf8_bin DEFAULT 'A', `col230` varchar(255) COLLATE utf8_bin DEFAULT 'Blah', `col231` varchar(255) COLLATE utf8_bin DEFAULT NULL, `col232` varchar(255) COLLATE utf8_bin DEFAULT NULL, `col233` varchar(255) COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (`col1`), KEY `col4` (`col4`), KEY `col5` (`col5`), KEY `CompositeIndex1` (`col1`,`col4`,`col5`), [4 other indexes not involving col1, col4, col5...] FULLTEXT KEY `col220` (`col220`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin //*******************************************************// CREATE TABLE `Table3` ( `col1` int(11) unsigned NOT NULL DEFAULT '0', `col300` varchar(255) COLLATE utf8_bin DEFAULT NULL, `latitude` varchar(25) COLLATE utf8_bin NOT NULL DEFAULT '0', `longitude` varchar(25) COLLATE utf8_bin NOT NULL DEFAULT '0', `col301` int(11) DEFAULT NULL, `static2` float(18,16) DEFAULT '0.0000000000000000', `static3` float(18,16) DEFAULT '0.0000000000000000', PRIMARY KEY (`col1`), KEY `latitude` (`latitude`), KEY `longitude` (`longitude`), KEY `static2` (`static2`), KEY `static3` (`static3`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin 如何设置为sort-buffer-size。根据{{​​3}},它不应设置在1M之上,否则它实际上可以减慢“37x”。 这可能是问题的一部分吗?

256K

另一方面,以下是来自伊万的最新查询# The MySQL database server configuration file. [mysqld] open-files-limit = 20000 thread-cache-size = 16 table-open-cache = 2048 table-definition-cache = 512 query-cache-type = 1 query-cache-size = 32M query-cache-limit = 1M sort-buffer-size = 1M read-buffer-size = 1M read-rnd-buffer-size = 8M join-buffer-size = 1M tmp-table-size = 64M max-heap-table-size = 64M back-log = 100 max-connections = 200 max-connect-errors = 10000 max-allowed-packet = 16M interactive-timeout = 600 wait-timeout = 180 net_read_timeout = 30 net_write_timeout = 30 back_log = 128 myisam-sort-buffer-size = 128M innodb-buffer-pool-size = 320M innodb-log-buffer-size = 4M innodb-log-file-size = 128M innodb-log-files-in-group = 2 innodb-file-per-table = 1 [mysqldump] max-allowed-packet = 16M 的结果:

EXPLAIN EXTENDED

另一方面,这里有一些非常奇怪的事情。以下版本的查询WITH id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE T1 ref PRIMARY,col2,col3,col1,CompositeIndex1,idx01 CompositeIndex1 2 const 92333 Using where; Using filesort 1 SIMPLE T3 eq_ref PRIMARY PRIMARY 4 T1.col1 1 Using where 1 SIMPLE T2 eq_ref PRIMARY,CompositeIndex1,idx_static1 PRIMARY 4 T1.col1 1 Using where 仅在 0.2秒

中完成
ORDER BY

基本上,此版本的查询执行SELECT STRAIGHT_JOIN T1 . * , T2 . * FROM Table3 AS T3 JOIN Table2 AS T2 ON T3.col1 = T2.col1 AND ( T2.col4 = 'Y' OR T2.col5 = 'Y' ) JOIN Table1 AS T1 ON T3.col1 = T1.col1 AND 3963.191 * ACOS( ( SIN( PI( ) * - 87.8819594 /180 ) * SIN( PI( ) * T3.latitude /180 ) ) + ( COS( PI( ) * - 87.8819594 /180 ) * COS( PI( ) * T3.latitude /180 ) * COS( PI( ) * T3.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 DESC FROM Table3 AS T3表1和2,而原始查询执行JOINFROM Table1 AS T1表2和3。

以下是上述查询的JOIN

EXPLAIN EXTENDED

请注意此查询实际上对Ivan的原始和新查询实际上只有id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE T3 ALL PRIMARY NULL NULL NULL 141923 100 Using where; Using temporary; Using filesort 1 SIMPLE T2 eq_ref PRIMARY,col4,col5,CompositeIndex1 PRIMARY 4 T3.col1 1 100 Using where 1 SIMPLE T1 eq_ref PRIMARY,col2,col3,col1,CompositeIndex1,idx01 PRIMARY 4 T2.col1 1 100 Using where filesorttemporary的对比。 如何快10倍?

更奇怪的是,切换filesort的顺序似乎并没有改善原始查询和Ivan的新查询。 为什么?

5 个答案:

答案 0 :(得分:5)

那么, 我建议你查询一些问题:

  1. 放入条件不加入相关的地方,请参阅第二个查询:

    AND(T1.col3喜欢&#39; X&#39;或T1.col3喜欢&#39; X-Y&#39;)

  2. 避免 OR 使用IN

  3. 避免喜欢使用 =

    AND T1.col3 IN(&#39; X&#39;,&#39; X-Y&#39;)

  4. 避免在

  5. 的位置进行计算

    创建一些要存储的新列:

    SIN(PI() * T3.latitude / 180)
    COS(PI() * table3.longitude / 180 - PI() * 37.1092162 / 180)
    COS(PI() * T3.latitude / 180) 
    
    1. 预先评估

      SIN(PI()* $ usersLatitude / 180) COS(PI()* $ usersLatitude / 180)

    2. 如果所有这些&#34;技巧&#34;不能文件排序强制索引

    3. mysql query index hint

      进一步添加

      要删除:

      ( T2.Col4 = 'Y' OR T2.Col5 = 'Y' )
      

      在这种情况下,您无法使用 IN ,因此请创建一个新列,该列是此表达式的结果。

      alter table table2 add static1 bit default 0;
      alter table add index idx_static1(static1);
      update table2 t2 set static1=1 where ( T2.Col4 = 'Y' OR T2.Col5 = 'Y' );
      
      alter table table3 add static2 float(18,16) default 0;
      update table3 set static2=SIN(PI() * T3.latitude / 180) where 1
      
      alter table table3 add static3 float(18,16) default 0;
      update table3 set static3 = COS(PI() * T3.latitude / 180) * COS(PI() * table3.longitude / 180 - PI() * 37.1092162 / 180)   where 1
      

      如果 table1.col2 的值很少

      alter table table1 change col2 col2 enum('A','B','C');
      

      如果 table1.col3 的值很少

      alter table table1 change col3 col3 enum('X','Y','X-Y');
      

      所有创建唯一索引所涉及的列 alter table add index idx01(col1,col2,col3)

      SELECT STRAIGHT_JOIN
            T1.*, 
            T2.*
         FROM 
            Table1 AS T1
               JOIN Table2 AS T2 ON T1.Col1 = T2.Col1
               JOIN Table3 as T3 ON T1.Col1 = T3.Col1
      
         WHERE static1=1 AND
                T1.Col2 = 'A'
            AND T1.col3 IN ( 'X', 'X-Y') 
            AND T1.Col1 != 1
            AND ACOS(  
                       ( 
                         $usersLatitude_sin_pi_fract180  * t3.static2 
                         + $usersLatitude_cos_pi_fract180  * t3.static3 
                       )   
                     ) <= 0,00252321929476 -- this's 10/3963.191
            ORDER BY T1.Col6
      

      您的评论建议我您在查询中使用了不同的排序规则(col1是latin1_swedish,col2是utf8)或者您的连接使用不同的排序规则(您的连接是utf-8并且您查询latin1_german列)所以当你查询时:

      t1.col2 = 'A'
      

      Mysql必须将每个值从utf-8转换为latin1。

      另请参阅mysql文档的collate section

      快速的方法是将所有(列,表,服务器,连接,客户端)转换为相同的校对singel字节,如果你不需要utf-8会更好。

      请注意我的类型错误或语法错误。

      进一步添加2

      我在测试数据库上重新创建了表格并修复了这些列: t1.col2,t2.col3 必须不可为空,t1.col1是主要的,不能为空。

      索引&#34; t1.CompositeIndex1&#34;应该只索引:col2,col3,col1;索引&#34;按顺序排列&#34;列是无用的或最差的。

      我创建了static1,我在t2.col1和t2.static1上创建了一个索引,但我没有使用DB中的6行(请参阅后面的解释)。 t2.static1也不能为空。

      我还将查询调整为列的整理:

      SELECT  T1.*, T2.* 
      FROM Table1 AS T1
               JOIN Table2 AS T2   ON ( T1.Col1 = T2.Col1   )
               JOIN Table3 as T3 ON T1.Col1 = T3.Col1
         WHERE  
               (  T1.Col2 =    'A'   collate utf8_bin  AND T1.col3 IN  ( 'X' collate utf8_bin , 'X-Y'  collate utf8_bin )   AND T1.Col1 != 1 )
      and T2.static1=1
            AND ACOS(  (   2.3  * T3.static2  + 1.2 * T3.static3  ) ) <= 0.00252321929476 
            ORDER BY T1.Col6
      

      以下是解释扩展

      +----+-------------+-------+--------+-----------------------------------+-----------------+---------+----------------+------+----------+-----------------------------+
      | id | select_type | table | type   | possible_keys                     | key             | key_len | ref            | rows | filtered | Extra                       |
      +----+-------------+-------+--------+-----------------------------------+-----------------+---------+----------------+------+----------+-----------------------------+
      |  1 | SIMPLE      | T1    | ref    | PRIMARY,col2,col3,CompositeIndex1 | CompositeIndex1 | 1       | const          |    1 |   100.00 | Using where; Using filesort |
      |  1 | SIMPLE      | T2    | eq_ref | PRIMARY,CompositeIndex1           | PRIMARY         | 4       | testdb.T1.col1 |    1 |   100.00 | Using where                 |
      |  1 | SIMPLE      | T3    | eq_ref | PRIMARY                           | PRIMARY         | 4       | testdb.T1.col1 |    1 |   100.00 | Using where                 |
      +----+-------------+-------+--------+-----------------------------------+-----------------+---------+----------------+------+----------+-----------------------------+ 
      

      列的相同:select_type,table,type,key,ref,filtered,Extra?

      我的优化目标是: - 适合少数指数中的条件 - 避免计算 - 避免整理转换 - 避免OR - 避免条件

      中的NULL

      现在是坏消息 似乎在表中您有~140K记录,并且如果查询涉及大量行,则查询使用顺序可能意味着使用filesort方法,因此最终答案可以通过@建议增加memsort缓冲区mavroprovato。

      进一步添加3

      为了唤起 key_buffer_size 的充分性,请参阅http://dba.stackexchange.com

      进一步添加4

      我认为只有Oracle中的某个人才能确切地说出会发生什么,但我的想法就是这样。

      我认为这个查询很奇怪:

      1. 所有表(t1,t2,t3)都由主键
      2. 连接
      3. 其他条件仅取决于calcs(t3.colX)
      4. 某些条件仅取决于索引(t1.colX)
      5. 因为1 from_table_rows&gt; = join1_table_rows&gt; = join2_table_rows,所以较少的行返回from table最快将是其他2个JOIN

        评估工作量的优化器将计算出类似的等式:

        effort = num_rows*key_size/index_cardinality
        

        (index_cardinality由phpmyadmin显示每个索引)

        由于2次努力是&gt; = num_rows

        我的查询 因为3,table1(来自表)返回92333行,table3(join1_table)减少为1(!)行,table2保持1行(努力~3)。

        您的查询 因为2你应该努力= 140000,但幸运的是你的calc只返回1个结果所以你的查询极其快速

        <强>笔画演示

        在您的查询中更改为&#34;&lt; = 10&#34; (在连接条件下)到&#34;&lt; = 1000&#34;或者更多,你会看到表现的指数下降。

        在我的查询中更改为&#34;&lt; = 10&#34; (在连接条件下)到&#34;&lt; = 1000&#34;或者你会看到性能的线性/对数减少。

        进一步添加5

        回答这个问题:sort-buffer-size是不是太大了?

        站在文章上,是的,尝试一些调,可能是你可以解决问题

        回答问题:不可能快速查询?

        恕我直言,这是可能的(即使排序缓冲区大小没有解决)。

        我的想法很简单,它可以在这个动词中恢复:&#34; cirlce很好但是方形更好&#34;。

        目前最大的基数在表3中的坐标上,但由于公式,没有索引适用。因此,不是搜索半径内的所有点,而是搜索&#34; square&#34;

        中的所有点。
        FROM table3
        ...
        WHERE (t3.latitude-0.15) < $usersLatitude AND  $usersLatitude < t3.latitude+0.15  
        AND t3.longitue - 0.15 < $usersLongitude AND   $usersLongitude < t3.longitue + 0.15
        

        所以你可以在(t3.latitude,t3.longitue)中创建一个索引。

        0.15 度应该是10英里。 当然,您应该在日变化的子午线附近和极点旁边修复计算

        如果您需要严格的半径,您可以使用半径公式重新连接table3(请参阅下面的示例)或(如果可能)执行(/详细说明)公式,直到您可以直接比较值与列

        FROM table3 t3
        JOIN table3 t3bis ON t3.id=t3bis.id
        ...
        WHERE (t3.latitude-0.15) < $usersLatitude AND  $usersLatitude < t3.latitude+0.15  
        AND t3.longitue - 0.15 < $usersLongitude AND   $usersLongitude < t3.longitue + 0.15
        AND 
        3963.191 
        * ACOS(  (SIN(PI() * $usersLatitude / 180) * SIN(PI() * t3bis.latitude / 180)) 
        + (  COS(PI() * $usersLatitude / 180) * COS(PI() * t3bis.latitude / 180) 
        * COS(PI() * t3bis.longitude / 180 - PI() * 37.1092162 / 180)
        )   
        ) <= 10 
        

        进一步添加6

        主题:编译后的函数做得更好

        使用RADIANS()函数

        degree * PI / 180 == radians(degree)
        

        使用mysql的GIS扩展

        请参阅此article about MySql GIS extension

答案 1 :(得分:1)

尝试第一个查询:

...
FROM 
    Table1 AS table1 USE INDEX (col6)
LEFT JOIN 
    Table2 AS table2   
...

答案 2 :(得分:0)

table1.col6的类型是什么? 它的场直径(最大值长度)是多少?

BTW预先计算的值,不依赖于字段值,如Ivan Buttinoni所建议的那样。它对订购没有帮助,但会使查询更快

答案 3 :(得分:0)

经过多次反复试验,我终于找到了解决问题的方法。

如果我们把整个WHERE子句 - 除了计算半径的部分 - 在原始查询之外,那么我们得到一个非常快的查询使用temporary更改JOIN的顺序:

SELECT * FROM
{
    SELECT
        col1, col2, col3, col4, col5, col6
    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 
) AS sub
WHERE
    col1 != '1' 
AND 
    col2 LIKE 'A' 
AND 
    (col3 LIKE 'X' OR col3 LIKE 'X-Y') 
AND 
    (col4 = 'Y' OR col5 = 'Y') 
ORDER BY 
    col6 DESC 

基本上,此查询首先根据半径获取所有3个表的JOIN结果,然后仅应用其余的过滤器以获得我们需要的结果。此版本的查询返回完全与原始查询相同的结果,但仅执行 0.2秒,而 3秒执行原始查询查询。

以下是EXPLAIN EXTENDED

id  select_type table   type    possible_keys   key key_len ref rows    filtered    Extra
1   PRIMARY <derived2>  ALL NULL    NULL    NULL    NULL    43  100 Using where; Using filesort
2   DERIVED T3  ALL PRIMARY NULL    NULL    NULL    143153  100 Using where
2   DERIVED users   eq_ref  PRIMARY,col1,idx01  PRIMARY 4   T3.col1 1   100 
2   DERIVED userProfile eq_ref  PRIMARY,CompositeIndex1 PRIMARY 4   users.col1  1   100 

我要感谢Ivan Buttinoni对此的出色工作。他找到了几种巧妙的方法来更快地进行查询。

故事的道德: putting it outside the main query不仅可以使ORDER BY条款变得更快,还可以通过放置部分WHERE来获得更快的查询在它之外的条款以及在这种情况下的条款。

答案 4 :(得分:0)

我认为你可以做三件事:

1)重构查询

2)在查询前面的Table1上应用ORDER BY

3)索引表1支持重构

也许这......

ALTER TABLE Table1 ADD INDEX col2_col6_ndx (col2,col6);
SELECT
    table1.*, 
    table2.*
FROM 
    (
        SELECT * FROM Table1
        WHERE col2='A' AND 
        ORDER BY col6 DESC
    ) AS table1
LEFT JOIN 
    (
        SELECT * FROM Table2
        WHERE (col4='Y' OR col5='Y')
    ) AS table2 
USING 
    (col1)
LEFT JOIN 
    Table3 as table3 
USING 
    (col1) 
WHERE 
    table1.col1 != '1' AND
    table1.col3 IN ('X','X-Y') AND
    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 
;

这是另一个尝试重构查询的变体,以便只收集密钥(创建更小的临时表),然后应用JOINS:

ALTER TABLE Table1 ADD INDEX col2613_ndx (col2,col6,col1,col3);
ALTER TABLE Table2 ADD INDEX col4_col1_ndx (col4,col1);
ALTER TABLE Table2 ADD INDEX col5_col1_ndx (col5,col1);
SELECT 
    table1.*, 
    table2.*
FROM
(
    SELECT table1.col1,table3.latitude,table3.longitude 
    FROM 
        (
            SELECT col1 FROM Table1 WHERE col2='A' AND
            AND col3 IN ('X','X-Y') ORDER BY col6 DESC
        ) AS table1
    LEFT JOIN 
        (
            SELECT col1 FROM Table2 WHERE col4='Y' UNION
            SELECT col1 FROM Table2 WHERE col5='Y'
        ) AS table2 
    USING (col1)
    LEFT JOIN Table3 as table3 USING (col1)
) col1_keys
LEFT JOIN Table1 table1 USING (col1)
LEFT JOIN Table2 table2 USING (col1)
WHERE 
    3963.191 * 
    ACOS(
    (SIN(PI() * $usersLatitude / 180) * SIN(PI() * col1_keys.latitude / 180)) 
    +
    (COS(PI() * $usersLatitude / 180) * COS(PI() * col1_keys.latitude / 180)
    * COS(PI() * col1_keys.longitude / 180 - PI() * 37.1092162 / 180))
    ) <= 10 
;

试一试!!!