2016-07-17 155 views
2

我已经数据库中,我存储日语词典:单词,阅读,标记,类型,在其他语言的含义(英语是这里最重要的,但也有一些其他的)和等等。MySQL查询慢where语句

现在,我想创建一个使用数据表的js插件的界面,让用户可以看到表,并使用一些过滤选项(如,只显示动词,​​或者找到含有“狗”的条目)。然而,我很苦恼,查询在使用过滤时会非常缓慢......我已经加速了很多,但它仍然不好。

这是我的基本查询:

select 
v.id, 
(
    select group_concat(distinct vke.kanji_element separator '; ') from vocabulary_kanji_element as vke 
    where vke.vocabulary_id = v.id 
) kanji_notation, 
(
    select group_concat(distinct vre.reading_element separator '; ') from vocabulary_reading_element as vre 
    where vre.vocabulary_id = v.id 
) reading_notation, 
(
    select group_concat(distinct vsg.gloss separator '; ') from vocabulary_sense_gloss as vsg 
    join vocabulary_sense as vs on vsg.sense_id = vs.id 
    join language as l on vsg.language_id = l.id and l.language_code = 'eng' 
    where vs.vocabulary_id = v.id 
) meanings, 
(
    select group_concat(distinct pos.name_code separator '; ') from vocabulary_sense as vs 
    join vocabulary_sense_has_pos as vshp on vshp.sense_id = vs.id 
    join part_of_speech as pos on pos.id = vshp.pos_id 
    where vs.vocabulary_id = v.id 
) pos 
from vocabulary as v 
join vocabulary_sense as vs on vs.vocabulary_id = v.id 
join vocabulary_sense_gloss as vsg on vsg.sense_id = vs.id 
join vocabulary_kanji_element as vke on vke.vocabulary_id = v.id 
join vocabulary_reading_element as vre on vre.vocabulary_id = v.id 
join language as l on l.id = vsg.language_id and l.language_code = 'eng' 
join vocabulary_sense_has_pos as vshp on vshp.sense_id = vs.id 
join part_of_speech as pos on pos.id = vshp.pos_id 
where 
-- pos.name_code = 'n' and 
(vsg.gloss like '%eat%' OR vke.kanji_element like '%eat%' OR vre.reading_element like '%eat%') 
group by v.id 
order by v.id desc 
-- limit 3900, 25 

Output是这样的:

|id | kanji_notation | reading_notation | meanings | pos | 
--------------------------------------------------------------- 
|117312| お手; 御手 |  おて  | hand; arm |n; int| 

Right现在(我的本地机器上工作)。如果没有WHERE语句,但极限,它的工作速度很快 - 约为0,140秒。但是,当文本过滤开启时,执行时间会减少至6,5秒,并且通常会高于此时间。首先在part_of_speech上进行过滤,就像5,5秒一样。 3秒就可以,但6秒太长了。

有1个155 897记录表vocabulary_sense_gloss,所以我认为这是不是很多。

CREATE TABLE `vocabulary_sense_gloss` (
    `id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `sense_id` MEDIUMINT(8) UNSIGNED NOT NULL, 
    `gloss` VARCHAR(255) NOT NULL, 
    `language_id` MEDIUMINT(8) UNSIGNED NOT NULL, 
    PRIMARY KEY (`id`), 
    INDEX `vocabulary_sense_gloss_vocabulary_sense_id` (`sense_id`), 
    INDEX `vocabulary_sense_gloss_language_id` (`language_id`), 
    FULLTEXT INDEX `vocabulary_sense_gloss_gloss` (`gloss`), 
    CONSTRAINT `vocabulary_sense_gloss_language_id` FOREIGN KEY (`language_id`) REFERENCES `language` (`id`), 
    CONSTRAINT `vocabulary_sense_gloss_vocabulary_sense_id` FOREIGN KEY (`sense_id`) REFERENCES `vocabulary_sense` (`id`) 
) 
COLLATE='utf8_general_ci' 
ENGINE=InnoDB 
; 

我想知道,有没有某种方法来优化它?或者,也许我应该改变我的数据库?我试图使用全文搜索,但速度并不快,而且似乎只能按全部条件工作,所以没用。使用'吃%'而不是'%eat%'的类似故事:它不会返回我想要的。

我试图vocabulary_sense_gloss两个表分 - 一个英语仅条款,以及其他与休息。由于用户通常会使用英语,它会使事情变得更快,但我不确定这是否是一种好方法。

此外,我试图将VARCHAR更改为CHAR。它似乎加快了执行时间,尽管表格大小增加了很多。

回答

3

这条WHERE条款的性能极差。

(vsg.gloss like '%eat%' OR 
vke.kanji_element like '%eat%' OR 
vre.reading_element like '%eat%') 

为什么?首先:column LIKE '%constant%'要求查询引擎检查column的每个可能的值。它不可能使用索引,因为常量搜索词中的前导%

第二:OR子句表示查询规划必须扫描结果三个不同的时间。

你打算如何改善这一点?这并不容易。您需要弄清楚如何使用column LIKE 'constant%'搜索条件消除常数中的前导%

一旦你这样做,你可以打败你丰富的加入结果的三重扫描,这样

 ... 
    WHERE v.id IN 
      (SELECT sense_id AS id 
       FROM vocabulary_sense_gloss 
      WHERE gloss LIKE 'eat%' 
               UNION 
      SELECT vocabulary_id AS id 
       FROM vocabulary_kanji_element 
      WHERE kanji_element LIKE 'eat%' 
               UNION 
      SELECT vocabulary_id AS id 
       FROM vocabulary_reading_element 
      WHERE reading_element LIKE 'eat%' 
      ) 

构建这将拉动id数相关的相关词语直接,而不是设置从多路连接的结果。为了加快速度,您的vocabulary_sense_gloss将需要(vocabulary_sense_gloss, sense_id)上的索引。其他两个表将需要相似的索引。