如何使 JOIN 查询中的 ORDER BY 更快?我没有尝试过

Posted

技术标签:

【中文标题】如何使 JOIN 查询中的 ORDER BY 更快?我没有尝试过【英文标题】:How to make ORDER BY in JOIN query faster? Nothing I have tried has worked 【发布时间】:2012-11-14 05:17:10 【问题描述】:

我有以下 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 秒

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

这是EXPLAIN EXTENDED WITHOUT 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 BY0.2 秒 变为3 秒。换句话说,将LIKE 替换为= 会使原始查询花费与添加ORDER BY 一样长!考虑到LIKE= 更有效,这怎么可能?也许这就是为什么ORDER BY 需要这么长时间的线索?

这是我迄今为止尝试过的(未成功):

1) 我尝试了 SELECT table1.col1 而不是 SELECT table1.*, table2.*,但它仍然需要 3 秒 才能完成。

2) 我尝试在Table1 中的col1col2col3col6 上添加复合索引,但它并没有提高执行速度。

3) 我尝试this solution 将查询作为子查询,然后在最后将ORDER BY 包裹在其外部,但它并没有提高执行速度。

4) 我尝试了以下版本的查询,但它没有改进任何东西,实际上使查询接管了 3 秒,甚至没有添加 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.

...

如何使用ORDER BY 让这个顽固的查询快速运行? 或者这是不可能的?


编辑:

SHOW CREATE TABLE 对所有 3 个表的结果:

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

编辑 2:

下面是我的 MySQL 配置文件。除其他事项外,请注意如何将sort-buffer-size 设置为1M。根据this 的说法,它不应该设置在256K 之上,否则它实际上会使速度减慢“37 倍”。 这可能是问题的一部分吗?

# 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 对来自 IVAN 的最新查询的结果:

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

在另一件事上,这里有一些非常奇怪的东西。以下版本的 WITH ORDER BY 查询只需 0.2 秒即可完成:

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 T3JOIN 表 1 和 2,而原始查询执行 FROM Table1 AS T1JOIN 表 2 和 3。

这是上面查询的EXPLAIN EXTENDED

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 而不是对来自 Ivan 的原始查询和新查询的 filesort怎么能快 10 倍?

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

【问题讨论】:

+1 表示您所做的努力以及您所尝试的信息 所有列都需要索引吗?对所有内容都有索引会对性能产生负面影响。我只会在您使用的列上放一个。 检查这个答案以提高 ORDER BY 查询的性能,尤其是关于调整 sort_buffer_sizeread_rnd_buffer_size 变量的值的部分。 ***.com/questions/4935391/… 您尚未尝试使用降序值存储创建索引。您只需要在索引中的列名后添加DESC 过度索引的负面影响应该只针对行的写入,而不是读取。 【参考方案1】:

嗯, 我建议您对查询进行一些重新设计:

    放入与连接无关的条件,见第二个查询:

    AND (T1.col3 LIKE 'X' OR T1.col3 LIKE 'X-Y')

    避免使用IN

    避免like使用=

    AND T1.col3 IN ('X' , 'X-Y')

    避免在 where 中计算

创建一些新的列来存储:

SIN(PI() * T3.latitude / 180)
COS(PI() * table3.longitude / 180 - PI() * 37.1092162 / 180)
COS(PI() * T3.latitude / 180) 

    预评估

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

    如果所有这些“技巧”都无法避免文件排序强制索引

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');

所有所涉及的列创建唯一索引 where 更改表添加索引 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。

如果您不需要 utf-8,一种快速的方法是将所有(列、表、服务器、连接、客户端)转换为相同的排序规则单字节会更好。

小心我的类型错误或我可以做的语法错误。

再添加 2 个

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

索引“t1.CompositeIndex1”应该只索引:col2,col3,col1;索引“排序依据”列是无用的或最差的。

我创建了 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?

我的优化目标是: - 适合少数索引中的 where 条件 - 避免计算 - 避免整理转换 - 避免或 - 在 where 条件下避免 NULL

现在是坏消息 似乎在表中你有大约 140K 记录,如果查询涉及很多行,使用 order 的查询可能意味着使用 filesort 方法,所以最终的答案可以是按照@mavroprovato 的建议增加 memsort 缓冲区。

再添加 3 个

要评估 key_buffer_size 的充分性,请参阅http://dba.stackexchange.com

再添加 4 个

我认为只有 Oracle 中的某个人才能准确说出会发生什么,但我有我的想法。

我认为这个查询很奇特:

    所有表 (t1,t2,t3) 都通过主键连接 其他条件仅取决于计算 (t3.colX) 某些条件仅取决于索引 (t1.colX)

由于 1 个 from_table_rows >= join1_table_rows >= join2_table_rows,因此返回 from 表的行数较少,最快将是其他 2 个 JOIN

评估工作量的优化器将计算一个类似的方程:

effort = num_rows*key_size/index_cardinality

(index_cardinality 由 phpmyadmin 在每个索引下显示)

因为 2 的努力是 >= num_rows

我的查询 由于 3 table1(来自 table)返回 92333 行,table3(join1_table)减少到 1(!)行,table2 保留 1 行(努力〜 3)。

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

演示

在您的查询从“

在我的查询从“

再添加 5 个

回答问题:sort-buffer-size 是否太大?

站在文章上,是的,试一试,也许能解决问题

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

恕我直言,这是可能的(即使 sort-buffer-size 无法解析)。

我的想法很简单,它可以在这个主题中继续:“圆形很好,但方形更好”。

目前最大的基数在table3中的坐标上,但是由于公式没有索引是适用的。因此,您可以搜索“正方形”内的所有点,而不是搜索半径内的所有点

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

【讨论】:

谢谢,但我不确定如何根据您的建议重新设置整个查询的样式。您能否发布一个满足您所有建议样式的查询版本?此外,关于 #3,在此查询中使用 = 代替 LIKE 会使其速度降低 10 倍,但我不知道为什么。 感谢您提供更多详细信息!我会尝试你建议的一切并报告。 Ivan,我按照您的建议执行了您的查询,但并没有提高性能。具体来说,我在Table3 中创建了新列来存储SIN(PI() * T3.latitude / 180)COS(PI() * table3.longitude / 180 - PI() * 37.1092162 / 180)COS(PI() * T3.latitude / 180),我预先评估了$usersLatitude_sin_pi_fract180$usersLatitude_cos_pi_fract180,将col2 和col3 更改为enum,[继续... ] 你能发布创建表的SQL吗? (所有表) 我实际上在查询中省略了( T2.Col4 = 'Y' OR T2.Col5 = 'Y' ),以避免使用static1,我为static2static3创建了一个索引,并且所有其他列都已经有一个索引,包括Table1 中用于 col1, col2, col3 的复合索引。 [续...]【参考方案2】:

尝试第一个查询:

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

【讨论】:

这并没有提高查询的性能。还有其他想法吗?【参考方案3】:

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

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

【讨论】:

table1.col6 的类型为 int(11)。所有数据类型都出现在上面的查询下方。如果您有任何其他想法,请告诉我! 我会尝试将 col6 放在第一位并“按 1 DESC 排序”。【参考方案4】:

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

如果我们将整个 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 结果,然后才应用其余的过滤器来获得我们需要的结果。此版本的查询返回的结果exact与我的原始查询相同,但执行时间仅为 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 在这方面的出色工作。他找到了几种巧妙的方法来加快查询速度。

故事的寓意:不仅ORDER BY 子句可以通过putting it outside the main query 更快,您还可以通过将部分WHERE 子句放在它之外来获得更快的查询在这种情况下很好。

【讨论】:

有时主要目标(最快的查询)比达到该目标的方式更重要。在这种情况下,“Gordian Knot”是 mysql 优化器而不是数据。甚至@RolandoMySQLDBA 也吐出查询来“驱动”优化器。这个查询用“FROM TABLE3”提醒我一次 感谢您的反馈,伊万!是的,它有点类似于FROM Table3 解决方案中的想法,但与那个解决方案不同的是,这个解决方案不使用Temporary(参见EXPLAIN)。基于EXPLAIN,您认为这个查询是否会像FROM Table3 解决方案一样出现“性能指数级下降”? 理论上是的,而且你有数据,所以你可以简单地增加搜索范围来验证它。 Ivan,你是说这不是一个好的解决方案,因为即使它不使用“临时”,它的性能也会呈指数级下降?如何通过增加搜索半径来验证它(我不知道该怎么做)?我认为这是一个完美的解决方案,但从你所说的看来情况并非如此,因为性能呈指数下降...... :( 首先是半径:改为 3963.191 * ACOS( ... ) 尝试 3963.191 * ACOS( ... ) b>,您将在半径 1000 英里范围内搜索【参考方案5】:

我认为您可以做三 (3) 件事:

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 
;

试试看!!!

【讨论】:

有趣的解决方案,我会尝试一下。 col2 条件(在Table1 中)是否应该放入Table2 的JOIN 中?同样,是col3 条件(也在Table1) meant to be put in the JOIN of Table3 中?最后,这个解决方案与我发布的解决方案相比如何?优点/缺点?请让我知道并再次感谢! 我更新了我的答案。我提出建议的原因是基于我在 2011 年 5 月 16 日回复的帖子:***.com/a/6023217/491757。我建议重新连接查询,以便在任何 JOIN 发生之前处理任何特定于表的 WHERE 子句。在那篇文章中,EXPLAIN PLAN 实际上比原来的更糟糕,但不仅比发布的其他查询更快,而且随着数据集的增长而更快。 老实说,我不知道我的查询是否会更快,除非尝试一下。至于你的答案,我没有注意到你甚至回答了它。 感谢您的澄清。但是,我仍然感到困惑,因为您在 Table3 的 JOIN 中使用来自 Table1col3,而在 Table3 中您有 SELECT col1, latitude,。您能否再次查看我的原始查询并确保您拥有正确版本的解决方案?我很想尝试一下,但我只是想确保我们做对了,以便它产生完全相同的结果(但希望更快)。谢谢! 这是我在凌晨 4:00 阅读问题所得到的。我没有正确阅读 CREATE TABLE 语句并将列放入正确的表上下文中。

以上是关于如何使 JOIN 查询中的 ORDER BY 更快?我没有尝试过的主要内容,如果未能解决你的问题,请参考以下文章

如何优化这个简单的 JOIN+ORDER BY 查询?

在 sql 查询中使用 LIMIT 和 ORDER BY 和 LEFT JOIN

在 JOIN 值上完成时 MySQL 慢 ORDER BY?

将 JOIN 和 ORDER BY 添加到查询后,性能意外提升

ORDER BY RAND()函数在mysql中执行需要很长时间[重复]

使用 JOIN 优化 SQL 查询的 ORDER BY 和 WHERE