【发布时间】:2019-08-17 09:24:58
【问题描述】:
我有一个包含超过 90,000 行的数据库表。
每一行都包含一个词,可以是形容词、副词、名词或动词(如类型列所示)。
我需要运行一个 MySQL 查询,它返回一小部分(例如 3 - 10)随机选择的名词,其中单词的长度在两个提供的数字之间(例如 4 - 8)。
我尝试过的查询的性能不是很好。
这是表结构:
CREATE TABLE `words` (
`id` int(11) NOT NULL,
`type` char(1) COLLATE utf8_unicode_ci NOT NULL,
`word` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`variations` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`free_writing_prompt` smallint(1) NOT NULL DEFAULT '0',
`word_length` smallint(5) NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ALTER TABLE `words`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `word_type` (`type`,`word`),
ADD KEY `type` (`type`),
ADD KEY `Word Length` (`word_length`);
ALTER TABLE `words`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
free_writing_prompt 列设置为 0 或 1,1 表示它是查询的有效选项,0 表示忽略它。目前,所有名词都将此列设置为 1,但计划是将许多单词更改为 0,因为它们不是应该返回的名词。
这是按类型划分的行数:
形容词:21,499
副词:4,475
名词:58,670
动词:8,978
这是我尝试的第一个查询:
SELECT word
FROM words
WHERE type='n'
AND free_writing_prompt=1
AND CHAR_LENGTH(word)>=4
AND CHAR_LENGTH(word)<=8
ORDER BY RAND()
LIMIT 3;
在此之后,我决定添加 word_length 列,它最初不是表定义的一部分,因为当我可以预先计算并存储该值时,对所有这些行使用 CHAR_LENGTH 函数似乎效率低下。
我还认为值得在 word_length 列上建立一个 Key 索引,但我不相信它真的有帮助。
然后我尝试了以下三个查询,但它们仍然需要大约 16 秒才能运行:
SELECT word
FROM words
WHERE type='n'
AND free_writing_prompt=1
AND word_length>=4
AND word_length<=8
ORDER BY RAND()
LIMIT 3;
SELECT word
FROM words
WHERE type='n'
AND free_writing_prompt=1
AND word_length IN (
SELECT word_length
FROM words
WHERE word_length>=4
AND word_length<=8
)
ORDER BY RAND()
LIMIT 3;
SELECT word
FROM words
WHERE word_length IN (
SELECT word_length
FROM words
WHERE type='n'
AND free_writing_prompt=1
AND word_length>=4
AND word_length<=8
)
ORDER BY RAND()
LIMIT 3;
然后我尝试了这个联合查询,它稍微快了一点(但可能不足以具有统计意义):
SELECT * FROM (
SELECT word FROM words WHERE type='n' AND free_writing_prompt=1 AND word_length=4
UNION
SELECT word FROM words WHERE type='n' AND free_writing_prompt=1 AND word_length=5
UNION
SELECT word FROM words WHERE type='n' AND free_writing_prompt=1 AND word_length=6
UNION
SELECT word FROM words WHERE type='n' AND free_writing_prompt=1 AND word_length=7
UNION
SELECT word FROM words WHERE type='n' AND free_writing_prompt=1 AND word_length=8
) a
ORDER BY RAND() LIMIT 3;
有人对如何优化此查询以提高运行时间有任何想法吗?
【问题讨论】:
-
拥有一个覆盖索引可能会有所帮助,例如:
(free_writing_prompt, type, word_length[, word]);如果执行 order-by-take-then-join,应该能够将word排除在外而几乎没有影响(即加入只需要进行 3 次探测) - 查询规划器说正在发生什么?另外,总的来说,我认为“ORDER BY RAND()”有“更有利”的解决方案。 -
注意覆盖索引的顺序是特定的:
free_writing_prompt和type都是直接相等的,而word_length是一个范围(不管它是如何写的;应该只使用AND word_length >= 4 AND word_length < 8并删除所有联合以覆盖操作;在这里通过多个直接相等来执行 SQL 没有好处) -
谢谢 - 我以前从来没有担任过覆盖索引的负责人。