0

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

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

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

但是,如果我尝试使用 SQLIN运算符将这些匹配到 200 万行的大表中,则需要很多分钟,

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;
4

3 回答 3

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;

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

于 2016-10-18T09:08:23.257 回答
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在排序和限制之前需要找到那么多行。

于 2016-10-19T21:00:05.940 回答
0

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

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

于 2016-10-18T10:07:28.310 回答