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
中找到那么多行。
我有一个较大的 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
中找到那么多行。