Почему запрос MySQL, оставленное соединение «значительно» быстрее моего внутреннего соединения - PullRequest
12 голосов
/ 22 ноября 2011

Я исследовал это, но до сих пор не могу объяснить, почему:

SELECT cl.`cl_boolean`, l.`l_name`
FROM `card_legality` cl
INNER JOIN `legality` l ON l.`legality_id` = cl.`legality_id`
WHERE cl.`card_id` = 23155

значительно медленнее, чем:

SELECT cl.`cl_boolean`, l.`l_name`
FROM `card_legality` cl
LEFT JOIN `legality` l ON l.`legality_id` = cl.`legality_id`
WHERE cl.`card_id` = 23155

115мс против 478мс. Они оба используют InnoDB, и есть определенные отношения. «Card_legality» содержит около 200 тыс. Строк, а таблица «legality» содержит 11 строк. Вот структура для каждого:

CREATE TABLE `card_legality` (
  `card_id` varchar(8) NOT NULL DEFAULT '',
  `legality_id` int(3) NOT NULL,
  `cl_boolean` tinyint(1) NOT NULL,
  PRIMARY KEY (`card_id`,`legality_id`),
  KEY `legality_id` (`legality_id`),
  CONSTRAINT `card_legality_ibfk_2` FOREIGN KEY (`legality_id`) REFERENCES `legality` (`legality_id`),
  CONSTRAINT `card_legality_ibfk_1` FOREIGN KEY (`card_id`) REFERENCES `card` (`card_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

И

CREATE TABLE `legality` (
  `legality_id` int(3) NOT NULL AUTO_INCREMENT,
  `l_name` varchar(16) NOT NULL DEFAULT '',
  PRIMARY KEY (`legality_id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1;

Я мог бы просто использовать LEFT-JOIN, но это не совсем правильно ... какие-нибудь мысли, пожалуйста?

UPDATE: В соответствии с просьбой, я включил результаты объяснения для каждого. Я запускал его ранее, но я не претендую на то, чтобы иметь полное понимание этого ..

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  cl  ALL PRIMARY NULL    NULL    NULL    199747  Using where
1   SIMPLE  l   eq_ref  PRIMARY PRIMARY 4   hexproof.co.uk.cl.legality_id   1   

И, внутреннее соединение:

id  select_type table   type    possible_keys   key key_len         ref                         rows    Extra
1   SIMPLE  l   ALL PRIMARY NULL    NULL    NULL    11  
1   SIMPLE  cl  ref PRIMARY,legality_id legality_id 4   hexproof.co.uk.l.legality_id    33799   Using where

Ответы [ 4 ]

9 голосов
/ 22 ноября 2011

Это из-за varchar на card_id. MySQL не может использовать индекс card_id в качестве card_id, как описано здесь преобразование типа mysql . Важная часть

Для сравнения строкового столбца с числом, MySQL не может использовать индекс на столбце, чтобы быстро найти значение. Если str_col является столбец индексированной строки, индекс не может быть использован при выполнении ищите в следующем утверждении:

ВЫБРАТЬ * ОТ tbl_name ГДЕ str_col = 1;

Причина этого в том, что есть много разных строк, которые могут преобразовать в значение 1, например «1», «1» или «1a».

Если вы измените свои запросы на

SELECT cl.`cl_boolean`, l.`l_name`
FROM `card_legality` cl
INNER JOIN `legality` l ON l.`legality_id` = cl.`legality_id`
WHERE cl.`card_id` = '23155'

и

SELECT cl.`cl_boolean`, l.`l_name`
FROM `card_legality` cl
LEFT JOIN `legality` l ON l.`legality_id` = cl.`legality_id`
WHERE cl.`card_id` = '23155'

Вы должны увидеть огромное улучшение скорости, а также увидеть другое ОБЪЯСНЕНИЕ.

Вот аналогичный (но более простой) тест, чтобы показать это:

> desc id_test;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id    | varchar(8) | NO   | PRI | NULL    |       |
+-------+------------+------+-----+---------+-------+
1 row in set (0.17 sec)

> select * from id_test;
+----+
| id |
+----+
| 1  |
| 2  |
| 3  |
| 4  |
| 5  |
| 6  |
| 7  |
| 8  |
| 9  |
+----+
9 rows in set (0.00 sec)

> explain select * from id_test where id = 1;
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
|  1 | SIMPLE      | id_test | index | PRIMARY       | PRIMARY | 10      | NULL |    9 | Using where; Using index |
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)


> explain select * from id_test where id = '1';
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | id_test | const | PRIMARY       | PRIMARY | 10      | const |    1 | Using index |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+
1 row in set (0.00 sec)

В первом случае Using where; Using index, а во втором Using index. Также ref это NULL или CONST. Излишне говорить, что второй лучше.

3 голосов
/ 22 ноября 2011

L2G суммировал это в значительной степени, хотя я подозреваю, что это может быть из-за типа varchar, используемого для card_id.

Я фактически распечатал эту информативную страницу для быстрого тестирования / профилирования,Вот быстрый метод профилирования бедняков:

Time a SQL on MySQL
Enable Profiling
mysql> SET PROFILING = 1
...
RUN your SQLs
...
mysql> SHOW PROFILES;

+----------+------------+-----------------------+
| Query_ID | Duration   | Query                 |
+----------+------------+-----------------------+
|        1 | 0.00014600 | SELECT DATABASE()     |
|        2 | 0.00024250 | select user from user |
+----------+------------+-----------------------+
mysql> SHOW PROFILE for QUERY 2;

+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000034 |
| checking query cache for query | 0.000033 |
| checking permissions           | 0.000006 |
| Opening tables                 | 0.000011 |
| init                           | 0.000013 |
| optimizing                     | 0.000004 |
| executing                      | 0.000011 |
| end                            | 0.000004 |
| query end                      | 0.000002 |
| freeing items                  | 0.000026 |
| logging slow query             | 0.000002 |
| cleaning up                    | 0.000003 |
+--------------------------------+----------+

Удачи, о, пожалуйста, опубликуйте свои выводы!

3 голосов
/ 22 ноября 2011

Я бы попробовал ОБЪЯСНИТЬ на оба этих запроса.Просто префикс каждого SELECT с EXPLAIN и запустить их.Это дает действительно полезную информацию о том, как mySQL оптимизирует и выполняет запросы.

0 голосов
/ 22 ноября 2011

Я почти уверен, что MySql имеет лучшую оптимизацию для левых соединений - на данный момент нет доказательств, подтверждающих это.

ETA: быстрый разведчик, и я не могу найти ничего конкретного, чтобы поддержать моюсмотреть так .....

...