Почему этот запрос выполняется так медленно? - PullRequest
4 голосов
/ 03 августа 2011

У меня есть две таблицы MySQL, скажем, A и B. A содержит только один столбец varchar (назовем его одним A1) с примерно 23000 записями.В таблице B (70000 записей) есть еще несколько столбцов, один из которых соответствует A1 из таблицы A (назовем это B1).Я хочу знать, какие значения в A не находятся в соответствующем столбце в B, поэтому я использую:

SELECT A1
FROM A
LEFT JOIN B
    ON A1 = B1
WHERE B1 IS NULL 

В обоих столбцах A1 и B1 определены индексы.Тем не менее этот запрос выполняется очень медленно.Я запустил объяснение, это вывод:

id  select_type table   type    possible_keys   key     key_len ref rows    Extra
1   SIMPLE      A       index   \N              PRIMARY 767     \N  23269   Using index
1   SIMPLE      B       ALL     \N              \N      \N      \N  70041   Using where; Not exists

ОБНОВЛЕНИЕ: SHOW CREATE TABLE для обеих таблиц (изменил исходные имена);

CREATE TABLE `A` (
  `A1` varchar(255) NOT NULL,
  PRIMARY KEY  (`A1`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8


CREATE TABLE `B` (
  `col1` int(10) unsigned NOT NULL auto_increment,
  `col2` datetime NOT NULL,
  `col3` datetime default NULL,
  `col4` datetime NOT NULL,
  `col5` varchar(30) NOT NULL,
  `col6` int(10) default NULL,
  `col7` int(11) default NULL,
  `col8` varchar(20) NOT NULL,
  `B1` varchar(255) default NULL,
  `col10` tinyint(1) NOT NULL,
  `col11` varchar(255) default NULL,
  PRIMARY KEY  (`col1`),
  KEY `NewIndex1` (`B1`)
) ENGINE=MyISAM AUTO_INCREMENT=70764 DEFAULT CHARSET=latin1

'другое редактирование: data_length и index_length из SHOW TABLE STATUS

table   data_length index_length
A       465380      435200
B       5177996     1344512

Ответы [ 5 ]

2 голосов
/ 03 августа 2011

Наборы символов двух столбцов, которые вы сравниваете в OUTER JOIN, различаются.Я не уверен, является ли это причиной, поэтому я проверил и получил следующие результаты:

SELECT A1
FROM A
LEFT JOIN B ON A1 = B1
WHERE B1 IS NULL

-- Table A..: 23258 rows, collation = utf8_general_ci
-- Table B..: 70041 rows, collation = latin1_swedish_ci
-- Time ....: I CANCELLED THE QUERY AFTER 20 MINUTES

-- Table A..: 23258 rows, collation = latin1_swedish_ci
-- Table B..: 70041 rows, collation = latin1_swedish_ci
-- Time ....: 0.187 sec

-- Table A..: 23258 rows, collation = utf8_general_ci
-- Table B..: 70041 rows, collation = utf8_general_ci
-- Time ....: 0.344 sec

Решение: сделать наборы символов двух таблиц (или, по крайней мере, двух столбцов) одинаковыми.

1 голос
/ 03 августа 2011

Этот запрос будет сканировать все строки таблицы A, но если у вас есть индекс для B1, то, скорее всего, он не будет сканировать таблицу B:

select A1
from A
where not exists (
    select *
    from B
    where B.B1 = A.A1
)

Перед запуском этого или исходного запроса вы можете попробовать запустить ANALYZE TABLE, чтобы обновить информацию о распределении ключей для этих таблиц:

ANALYZE TABLE A, B

Если это не поможет, вы можете попробовать поиграть с индексами, например:

select A1
from A ignore index (PRIMARY)
where not exists (
    select *
    from B force index (NewIndex1)
    where B.B1 = A.A1
)
0 голосов
/ 03 августа 2011

Если я использую ваши операторы CREATE TABLES и запускаю EXPLAIN для вашего оператора SELECT, я получаю следующий результат:

id  select_type  table  type  possible_keys  key       key_len  ref  rows  Extra  
1   SIMPLE       A      index NULL           PRIMARY   767      NULL 2     Using index 
1   SIMPLE       B      index NULL           NewIndex1 258      NULL 4     Using where; Using index 

В моей версии MySQL (5.1.41) индекс используется должным образом, поэтому я думаю, что это может быть уже исправленной ошибкой в ​​MySQL, если предположить, что ваш индекс установлен так же, как в опубликованном операторе создания таблицы. Какую версию MySQL вы используете?

0 голосов
/ 03 августа 2011

попробуйте этот запрос:

SELECT B1
FROM B
WHERE not B1  in (
  select A1
  from a
)
0 голосов
/ 03 августа 2011

Кажется, A1 и B1 - большие поля.

Вы создали индексы для A1 и B1

Убедитесь, что они проиндексированы!

SELECT A1
FROM A
WHERE A1 NOT IN (
    SELECT B1 AS A1 From B;
)
...