2016-10-18 6 views
0

У меня есть довольно большой стол (2 миллиона строк), с колонкой, содержащих текстовые идентификаторы (это латинские названия видов, homo_sapiens, Tyranosaurus_rex и т.д.)MySql проблемы с текстом спичек с использованием IN заявления

У меня есть еще один таблицу, содержащую латинские имена и «общие» имена видов, и я могу запросить это, чтобы получить небольшой выбор (~ 140 имен) латинских имен, некоторые из которых отображаются в первую таблицу. Я хочу получить строки в первой таблице, чьи имена точно соответствуют этому небольшому выбору. Запрос я использую, чтобы получить небольшой выбор (только 140 строк) работает быстро, так как общее название FULLTEXT MySql»» индекс

select distinct latin_name from common_names_table 
    where match(common_name) against('+*mo*' in boolean mode) 

Но если я пытаюсь использовать оператор SQL IN, чтобы соответствовать это в большой, 2 миллиона таблицы строк, это занимает много минут,

select latin_name,popularity from big_table 
where latin_name in (
    select distinct latin_name from common_names_table 
    where match(common_name) against('+*mo*' in boolean mode) 
) 
ORDER BY popularity DESC LIMIT 50; 

это верно, даже если я поставил как полный текст и нормальный индекс на latin_name колонке.

CREATE FULLTEXT INDEX name_fulltext_index ON big_table (latin_name); 
CREATE INDEX name_index   ON big_table (latin_name); 

Как я могу ускорить это? Есть ли проблема с использованием оператора IN с индексированными текстовыми полями? Если да, существует ли какой-то специальный индекс «точного соответствия», который я могу использовать для текстовых полей? Поля latin_name имеют тип «VARCHAR» и максимальную длину 190 в маленькой таблице и 200 в целом, если это имеет значение.

Спасибо за любую помощь


В соответствии с просьбой - вот определение таблицы:

CREATE TABLE `big_table` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `parent` int(11) NOT NULL, 
    `latin_name` varchar(200) DEFAULT NULL, 
    `popularity` double DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `pop_index` (`popularity`), 
    KEY `name_index` (`latin_name`), 
    FULLTEXT KEY `name_fulltext_index` (`latin_name`) 
) ENGINE=InnoDB AUTO_INCREMENT=1781766 DEFAULT CHARSET=utf8; 

CREATE TABLE `common_name_table` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `latin_name` varchar(190) CHARACTER SET utf8mb4 NOT NULL DEFAULT '', 
    `common_name` varchar(190) CHARACTER SET utf8mb4 NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `name_index` (`latin_name`), 
    FULLTEXT KEY `common_name_index` (`common_name`) 
) ENGINE=InnoDB AUTO_INCREMENT=2024 DEFAULT CHARSET=utf8; 
+0

если возможно, можете ли вы разместить определения таблиц, чтобы мы могли видеть, где присоединиться? –

+0

Хмм, я попробую, но я упростил таблицы для вопроса, так что это не так просто. – user2667066

+0

@krishKM спасибо за любую помощь - добавлены определения на вопрос – user2667066

ответ

0

AHA - благодаря @krishKM, требующему определений, я нашел проблему. Кодировка набора символов для двух столбцов, которые я пытаюсь сопоставить, различна: одна - это UTF8 по умолчанию в mySQL, другая - «правильная» 4-байтовая кодировка utf8mb4.

Если я установил latin_name в ту же кодировку символов в обеих таблицах, запрос займет ~ 20 миллисекунд вместо 5 минут.

1

Вы можете попробовать присоединиться вместо 'IN':

select 
b.latin_name, 
b.popularity 
from 
(
    select distinct latin_name from common_names_table 
    where match(common_name) against('+*mo*' in boolean mode) 
) a 
left join big_table as b on (a.latin_name=b.latin_name) 
where b.latin_name IS NOT NULL 
ORDER BY b.popularity DESC LIMIT 50; 

Левое соединение (где правая сторона не равна нулю), вероятно, будет быстрее, чем внутреннее соединение

+0

Спасибо. Как ни странно, это, похоже, также занимает много, много минут, чтобы бежать, как и внутреннее соединение. Но если я отменил объединение и сделаю 'big_table left join (select ...)', это очень быстро, если я не укажу 'a.latin_name не null '. Я не могу понять, почему просто замена соединения будет иметь такой огромный эффект. – user2667066

+0

Не видел определения таблиц. Я думаю, что utf8mb4 во второй таблице вызывает проблему с кодировкой. Возможно, вы можете его явно преобразовать в подзапрос, и это ускорит его. С другой стороны, если это работает наоборот, проблема решена :). В первом запросе большая таблица преобразуется. Если вы поменяете таблицы так, как вы это сделали, он преобразует маленькую таблицу, и это быстрее – verhie

1

LEFT не нужно:

select b.latin_name, b.popularity 
    from 
    (
     SELECT distinct latin_name 
      from common_names_table 
      where match(common_name) against('+*mo*' in boolean mode) 
    ) cn 
    join big_table as b ON (cn.latin_name = b.latin_name) 
    ORDER BY b.popularity DESC 
    LIMIT 50; 

Чтобы почувствовать, почему это происходит медленно, выполнять

 SELECT COUNT(distinct latin_name) 
      from common_names_table 
      where match(common_name) against('+*mo*' in boolean mode); 

что многие строки должны быть найдены в big_table до рода и предел.