mySQL 使用 IN 语句进行文本匹配的问题

mySQL problems with text matches using IN statement

我有一个较大的 table(200 万行),有一列包含文本标识符(这些是物种的拉丁名称,Homo_sapiens、Tyranosaurus_rex 等)

我有另一个 table 包含拉丁名称和 "common" 物种名称,我可以查询它以获得一小部分(约 140 个名称)拉丁名称,其中一些映射到第一个table。我想获取第一个 table 中的行,这些行的名称恰好映射到这个小的选择。我用来获取小选择(仅 140 行)的查询运行速度很快,因为通用名称有一个 mySQL 'FULLTEXT" 索引

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

但是如果我尝试使用 SQL IN 运算符将它们匹配到 200 万行的大行中 table,这需要很多分钟,

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 运算符是否有问题?如果是这样,是否有某种特殊类型的 "exact match" 索引可用于文本字段? latin_name 字段的类型都是 "VARCHAR" 并且最大长度在小 table 中为 190,在大中为 200,如果有任何区别的话。

感谢您的帮助


根据要求 - 以下是 table 定义:

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;

您可以尝试连接而不是 '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;

左连接(右侧不为空)可能比内连接更快

AHA - 感谢@krishKM 询问定义,我发现了问题。我尝试匹配的两列的字符集编码不同:一个是mySQL中的默认UTF8,另一个是'proper' 4字节utf8mb4编码。

如果我在两个表中将 latin_name 设置为相同的字符编码,则查询需要大约 20 毫秒而不是 5 分钟。

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 中找到那么多行。