MySQL语句

时间:2016-07-17 15:36:14

标签: mysql performance

我在数据库中存储日语词典:单词,读数,标签,类型,其他语言的含义(英语在这里最重要,但也有其他一些)等等。

现在,我想使用Datatables js插件创建一个接口,因此用户可以看到表并使用一些过滤选项(比如,只显示动词,​​或查找包含“dog”的条目)。然而,我正在努力解决使用过滤时查询速度相当慢的问题...我已经加快了速度,但仍然不是很好。

这是我的基本查询:

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

输出是这样的:

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

现在(在我的本地机器上工作),如果没有WHERE语句,但有限制,它可以快速运行 - 大约0,140秒。但是当启用文本过滤时,执行时间最多可达6,5秒,通常高于。首先在part_of_speech上进行过滤,就像5,5秒一样。 3秒就可以,但是6太长了。

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

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%'代替'%eat%'的类似故事:它不会返回我想要的东西。

我试图将vocabulary_sense_gloss分成两个表 - 一个用英语单词表示,另一个表用其他表。由于用户通常会使用英语,这会让事情变得更快,但我不确定这是不是一个好方法。

另外,我试图将VARCHAR更改为CHAR。它似乎加快了执行时间,虽然表格大小上升了很多。

1 个答案:

答案 0 :(得分: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个数字,而不是多路JOIN的结果。为了更快,您的词汇表_sense_gloss将需要(vocabulary_sense_gloss, sense_id)上的索引。其他两个表将需要类似的索引。