Подтвердить что ты не робот

Как сделать запрос ORDER BY в JOIN быстрее? Ничего я не пробовал, работал

У меня есть следующий запрос 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.

Я также сделал 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, col2, col3 и col6 в Table1, но это не улучшило скорость выполнения.

3) Я попробовал это решение сделать запрос в качестве подзапроса и затем обернуть 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? Или это просто невозможно?


EDIT:

Результаты SHOW CREATE TABLE для всех трех таблиц:

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, или он может фактически замедлить работу с помощью "37x". Может ли быть частью проблемы?

# 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 ПОСЛЕДНИЕ ВОПРОСЫ ИЗ ИВАНА:

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 T3 и JOIN таблицы 1 и 2, тогда как исходный запрос имеет FROM Table1 AS T1 и JOIN таблицы 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

Обратите внимание, как этот запрос фактически выполняет ОБА filesort и temporary вместо простого filesort в исходных и новых запросах от Ivan. Как это может быть на 10 раз быстрее?

Даже незнакомец, переключая порядок JOIN, похоже, не улучшает ни первоначальный запрос, ни более новые запросы от Ивана. Почему это?

4b9b3361

Ответ 1

После долгих проб и ошибок я наконец нашел решение своего вопроса.

Если мы поместим все предложение 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 

По существу, этот запрос сначала получает результаты 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 

Я хотел поблагодарить Иван Буттинони за отличную работу над этим. Он нашел несколько умных способов сделать этот запрос еще быстрее.

Мораль истории: это не просто предложение ORDER BY, которое может быть сделано быстрее помещая его вне основного запроса, вы также можете получить более быстрый запрос, поместив часть предложения WHERE вне его также в таких ситуациях, как этот.

Ответ 2

Ну, Я предлагаю вам несколько повторений запроса:

  • введите, где условия не связаны, см. второй запрос:

    И (T1.col3 LIKE 'X' ИЛИ ​​T1.col3 LIKE 'X-Y')

  • избегать ИЛИ использовать IN

  • избегать как использовать =

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

  • избегать вычислений, в которых

создайте несколько новых столбцов для хранения:

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. если все эти "трюки" не могут избежать принудительного сортировки файлов, индексы

подсказка индекса запросов mysql

ДАЛЬНЕЙШЕЕ ДОБАВЛЕНИЕ

чтобы удалить:

( 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 добавить индекс 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 10/3963.191
      ORDER BY T1.Col6

Ваш комментарий подсказывает мне, что у вас другая сортировка в запросе (col1 - latin1_swedish, а col2 - utf8), или ваше соединение использует другую сортировку (ваше соединение - utf-8, и вы запрашиваете столбец latin1_german), поэтому, когда вы запрос:

t1.col2 = 'A'

Mysql должен преобразовать из utf-8 в latin1 каждое значение.

См. также раздел сортировки документации mysql.

Быстрый способ конвертировать все (столбец, таблица, сервер, соединение, клиент) в один и тот же набор синтаксических байтов будет лучше, если вам не нужен utf-8.

Будьте осторожны с ошибкой моего типа или синтаксической ошибкой, которую я мог бы сделать.

ДАЛЬШЕ ДОБАВИТЬ 2

Я воссоздал таблицы в тестовой БД, и я исправил эти столбцы: t1.col2, t2.col3 не может быть нулевым, t1.col1 является первичным и не может быть null.

Индекс "t1.CompositeIndex1" должен индексироваться только: col2, col3, col1; Индексировать столбец "порядок" не полезен или худший.

Я создаю static1, и я создаю индекс на t2.col1 и t2.static1, но с моими 6 строками в БД не используется (см. объяснение позже). t2.static1 также не должен иметь значение NULL.

Я также адаптирую запрос к сортировке столбцов:

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                 |
+----+-------------+-------+--------+-----------------------------------+-----------------+---------+----------------+------+----------+-----------------------------+ 

Является ли это тем же для colums: select_type, table, type, key, ref, filter, Extra?

Мои цели оптимизации: - соответствовать условиям, в которых есть несколько индексов - избегать расчетов - избежать преобразования сортировки - избегать ИЛИ - избегать NULL, где условие

Теперь плохая новость Похоже, что в таблицах, которые вы используете ~ 140K записей, а запрос с использованием порядка может подразумевать использование подхода fileort, если запрос включает в себя много строк, поэтому окончательный ответ может увеличить буфер memsort, как подсказывает @mavroprovato.

ДАЛЬШЕ ДОБАВИТЬ 3

Чтобы оценить адекватность key_buffer_size, см. http://dba.stackexchange.com

ДАЛЬШЕ ДОБАВИТЬ 4

Я думаю, что только кто-то из Oracle может точно сказать, что происходит, но у меня есть идея.

Я думаю, что этот запрос свойственен:

  • все таблицы (t1, t2, t3) объединяются с помощью первичного ключа
  • другие условия зависят только от calcs (t3.colX)
  • некоторые условия зависят только от индекса (t1.colX)

Из-за 1 from_table_rows >= join1_table_rows >= join2_table_rows, так что меньше строк вернет из таблицы быстрее, будут 2 других JOINs

Оптимизатор для оценки усилия рассчитает аналогичное уравнение:

effort = num_rows*key_size/index_cardinality

(index_cardinality отображается по phpmyadmin рядом с каждым индексом)

Из-за 2 усилий >= num_rows

Мой запрос из-за 3 таблица1 (из таблицы) возвращает 92333 строк, таблица3 (join1_table) уменьшает до 1 (!) строки, таблица2 сохраняет 1 строку (усилие ~ 3).

Ваш запрос из-за 2 у вас должно получиться усилие = 140000, но, к счастью, для вас результат calc возвращает только 1 результат, чтобы ваш запрос был экстремально быстрым.

Demostration

В вашем запросе, изменяющемся от "< = 10" (в состоянии соединения) до "< = 1000" или более, вы увидите экспоненциальное снижение производительности.

В моем запросе, изменяющемся от "< = 10" (в состоянии соединения) до "< = 1000" или более, вы увидите линейное/логарифмическое снижение производительности.

ДАЛЬШЕ ДОБАВИТЬ 5

ответ на вопрос: слишком большой размер буфера сортировки?

стоя на статью, да, попробуйте какую-нибудь мелодию, возможно, вы сможете решить проблему.

ответ на вопрос: невозможно выполнить быстрый запрос?

IMHO нет, это возможно (даже если размер сортировочного буфера не устраняется).

Моя идея довольно проста, и она может возобновиться в этом mot: "cirlce хорош, но квадрат лучше".

В настоящий момент наибольшая мощность определяется координатами в таблице 3, но из-за формулы не применяется индекс. Таким образом, вместо поиска всех точек внутри радиуса вы можете искать все точки внутри "квадрата"

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 миль. Конечно, вы должны исправить вычеты вблизи меридиана смены дня и рядом с полюсами

Если вам нужен строго радиус, вы можете повторно подключиться к таблице 3 с помощью формулы радиуса (см. пример ниже) или , если возможно выполнить (/разработать) формулу, пока вы не сможете сравнить непосредственно значения с столбцами.

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)

Использование расширения GIS для mysql

Смотрите статью о расширении GIS MySql

Ответ 3

Попробуйте выполнить первый запрос:

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

Ответ 4

Каков тип table1.col6? Каков его диаметр поля (максимальная длина)?

Предварительно вычислить значения BTW, которые не зависят от значений полей, как предположил Иван Буттинони. Это не поможет при заказе, но быстрее сделает запрос

Ответ 5

Есть три (3) вещи, которые я вижу, вы можете сделать:

1) Рефакторинг запроса

2) Примените ORDER BY в таблице 1 ранее в запросе

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 
;

Дайте ему попробовать!!!