Очень надеюсь, что какой-то вихрь производительности может объяснить мне, почему одно соединение приводит к тому, что запрос становится в 10 раз медленнее. (Также, пожалуйста, не смейтесь над размером этого запроса! Я хотел получить весь каталог в моей базе данных для вывода одним запросом. Я не уверен, будет ли быстрее разбить его на меньшие запросы, но это не кажется правильным.)
SELECT `c`.`categoryID`,
`cl`.`name` AS `category_name`,
`v`.*,
TRUE AS `categoried`,
GROUP_CONCAT(DISTINCT t_v.iso_3166_1_alpha_2) AS `video_territories`,
GROUP_CONCAT(DISTINCT t_c.iso_3166_1_alpha_2) AS `category_territories`,
`vl`.*,
GROUP_CONCAT(DISTINCT kl.name) AS `keywords`
FROM `tblCategories` AS `c`
INNER JOIN `tblCategoryLocalisedData` AS `cl` ON c.categoryID = cl.categoryID
LEFT JOIN `tblCategoryDurations` AS `cd` ON c.categoryID = cd.categoryID
LEFT JOIN `tblCategoryRules` AS `cr` ON c.categoryID = cr.categoryID
LEFT JOIN `tblCategoryVideos` AS `cv` ON c.categoryID = cv.categoryID
LEFT JOIN `tblVideos` AS `v` ON cv.videoID = v.videoID
LEFT JOIN `tblVideoTerritories` AS `vt` ON vt.videoID = v.videoID
LEFT JOIN `tblCategoryTerritories` AS `ct` ON ct.categoryID = c.categoryID
INNER JOIN `tblTerritories` AS `t_v` ON t_v.territoryID = vt.territoryID
INNER JOIN `tblTerritories` AS `t_c` ON t_c.territoryID = ct.territoryID
INNER JOIN `tblVideoLocalisedData` AS `vl` ON vl.videoID = v.videoID
LEFT JOIN `tblVideoKeywords` AS `vk` ON v.videoID = vk.videoID
LEFT JOIN `tblKeywords` AS `k` ON vk.keywordID = k.keywordID
LEFT JOIN `tblKeywordLocalisedData` AS `kl` ON kl.keywordID = k.keywordID
INNER JOIN `tblLanguages` AS `l`
WHERE (cv.disabled IS NULL)
AND (cd.start_date < NOW() OR cd.start_date IS NULL)
AND (cd.end_date > NOW() OR cd.end_date IS NULL)
AND (cr.name IS NULL)
AND (l.languageID = cl.languageID OR cl.languageID IS NULL)
AND (l.languageID = kl.languageID OR kl.languageID IS NULL)
AND (l.languageID = vl.languageID OR vl.languageID IS NULL)
AND (l.iso_639_1 = 'en')
GROUP BY `v`.`videoID`, `c`.`categoryID`
ORDER BY `c`.`categoryID` ASC
Когда я запускаю приведенный выше запрос, требуется 1 целая секунда для завершения Я попытался запустить EXPLAIN на него, и он дал мне это:
+----+-------------+-------+--------+--------------------------------------------------------------------------------------+-----------------------------------------+---------+------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+--------------------------------------------------------------------------------------+-----------------------------------------+---------+------------------------+------+----------------------------------------------+
| 1 | SIMPLE | cv | ALL | fk_tblCategoryVideos_tblCategories1,fk_tblCategoryVideos_tblVideos1 | NULL | NULL | NULL | 2 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | c | eq_ref | PRIMARY | PRIMARY | 4 | db.cv.categoryID | 1 | Using index |
| 1 | SIMPLE | cd | ref | fk_tblCategoryDurations_tblCategories | fk_tblCategoryDurations_tblCategories | 4 | db.cv.categoryID | 1 | Using where |
| 1 | SIMPLE | cr | ref | fk_tblCategoryRules_tblCategories1 | fk_tblCategoryRules_tblCategories1 | 4 | db.cv.categoryID | 1 | Using where; Not exists |
| 1 | SIMPLE | vt | ref | fk_tblVideoTerritories_tblVideos1,fk_tblVideoTerritories_tblTerritories1 | fk_tblVideoTerritories_tblVideos1 | 4 | db.cv.videoID | 1 | Using where |
| 1 | SIMPLE | t_v | eq_ref | PRIMARY | PRIMARY | 4 | db.vt.territoryID | 1 | |
| 1 | SIMPLE | v | eq_ref | PRIMARY | PRIMARY | 4 | db.vt.videoID | 1 | Using where |
| 1 | SIMPLE | vk | ref | fk_tblVideoKeywords_tblVideos1 | fk_tblVideoKeywords_tblVideos1 | 4 | db.cv.videoID | 6 | |
| 1 | SIMPLE | k | eq_ref | PRIMARY | PRIMARY | 4 | db.vk.keywordID | 1 | Using index |
| 1 | SIMPLE | kl | ref | fk_tblKeywordLocalisedData_tblKeywords1 | fk_tblKeywordLocalisedData_tblKeywords1 | 4 | db.k.keywordID | 1 | |
| 1 | SIMPLE | cl | ALL | fk_tblCategoryLocalisedData_tblCategories1,fk_tblCategoryLocalisedData_tblLanguages1 | NULL | NULL | NULL | 5 | Using where; Using join buffer |
| 1 | SIMPLE | l | eq_ref | PRIMARY | PRIMARY | 4 | db.cl.languageID | 1 | Using where |
| 1 | SIMPLE | ct | ALL | fk_tblCategoryTerritories_tblCategories1,fk_tblCategoryTerritories_tblTerritories1 | NULL | NULL | NULL | 2 | Using where; Using join buffer |
| 1 | SIMPLE | vl | ALL | fk_tblVideoLocalisedData_tblLanguages1,fk_tblVideoLocalisedData_tblVideos1 | NULL | NULL | NULL | 9 | Using where; Using join buffer |
| 1 | SIMPLE | t_c | eq_ref | PRIMARY | PRIMARY | 4 | db.ct.territoryID | 1 | |
+----+-------------+-------+--------+--------------------------------------------------------------------------------------+-----------------------------------------+---------+------------------------+------+----------------------------------------------+
Однако я понятия не имею, что это значит. Как мне устранить это? К счастью, я знаю, какие части запроса вызывают значительное замедление. Если я удаляю соединения из tblVideoTerritories (vt) в tblTerritories (t_v) или из tblCategoryTerritories (ct) в tblTerritories (t_c), тогда все значительно ускоряется. Я подумал, что для начала это могло произойти из-за GROUP_CONCAT или DISTINCT, но я попытался удалить их, и они почти не изменились. Похоже, что проблема с производительностью вызвана двойным присоединением к одной и той же таблице tblTerritories . Если у меня есть только одно из этих объединений, выполнение запроса занимает всего 0,1 секунды или 0,2 секунды - это еще долго, но лучше начать!
Что я хочу знать, так это как я могу решить эту проблему с производительностью? Почему объединение в одну и ту же таблицу дважды приводит к увеличению длины запроса в 10 раз?!
Спасибо за любую помощь!
редактирование:
ШОУ CREATE TABLE на tblVideoTerritories дает мне это:
CREATE TABLE `tblVideoTerritories` (
`videoTerritoryID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`videoID` int(10) unsigned NOT NULL,
`territoryID` int(10) unsigned NOT NULL,
PRIMARY KEY (`videoTerritoryID`),
KEY `fk_tblVideoTerritories_tblVideos1` (`videoID`),
KEY `fk_tblVideoTerritories_tblTerritories1` (`territoryID`),
CONSTRAINT `fk_tblVideoTerritories_tblTerritories1` FOREIGN KEY (`territoryID`) REFERENCES `tblTerritories` (`territoryID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_tblVideoTerritories_tblVideos1` FOREIGN KEY (`videoID`) REFERENCES `tblVideos` (`videoID`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
ПОКАЗАТЬ СОЗДАТЬ СТОЛ на tblCategoryTerritories дает мне это:
CREATE TABLE `tblCategoryTerritories` (
`categoryTerritoryID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`categoryID` int(10) unsigned NOT NULL,
`territoryID` int(10) unsigned NOT NULL,
PRIMARY KEY (`categoryTerritoryID`),
KEY `fk_tblCategoryTerritories_tblCategories1` (`categoryID`),
KEY `fk_tblCategoryTerritories_tblTerritories1` (`territoryID`),
CONSTRAINT `fk_tblCategoryTerritories_tblCategories1` FOREIGN KEY (`categoryID`) REFERENCES `tblCategories` (`categoryID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_tblCategoryTerritories_tblTerritories1` FOREIGN KEY (`territoryID`) REFERENCES `tblTerritories` (`territoryID`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
ТАБЛИЦА СОЗДАНИЯ ШОУ на tblTerritories дает мне следующее:
CREATE TABLE `tblTerritories` (
`territoryID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`iso_3166_1_alpha_2` char(2) COLLATE utf8_unicode_ci DEFAULT NULL,
`iso_3166_1_alpha_3` char(3) COLLATE utf8_unicode_ci DEFAULT NULL,
`defaultLanguageID` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`territoryID`),
KEY `fk_tblTerritories_tblLanguages1` (`defaultLanguageID`),
KEY `iso_3166_1_alpha_2` (`iso_3166_1_alpha_2`),
CONSTRAINT `fk_tblTerritories_tblLanguages1` FOREIGN KEY (`defaultLanguageID`) REFERENCES `tblLanguages` (`languageID`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
edit2:
Причина двойного присоединения к одной и той же территории состоит в том, что мне нужно создать два отдельных списка территорий, используя GROUP_CONCAT в верхней части запроса. Мне нужен один для видео и один для категории, к которой он относится.
Edit3:
Интересно, что если я урежу запрос до его оголенных частей, он будет очень быстрым (0,00 секунды) даже при двухкратном соединении с одной и той же таблицей:
SELECT `c`.`categoryID`,
`v`.`videoID`,
GROUP_CONCAT(DISTINCT t_v.iso_3166_1_alpha_2) AS `video_territories`,
GROUP_CONCAT(DISTINCT t_c.iso_3166_1_alpha_2) AS `category_territories`
FROM `tblCategories` AS `c`
LEFT JOIN `tblCategoryVideos` AS `cv` ON c.categoryID = cv.categoryID
LEFT JOIN `tblVideos` AS `v` ON cv.videoID = v.videoID
LEFT JOIN `tblVideoTerritories` AS `vt` ON vt.videoID = v.videoID
LEFT JOIN `tblCategoryTerritories` AS `ct` ON ct.categoryID = c.categoryID
INNER JOIN `tblTerritories` AS `t_v` ON t_v.territoryID = vt.territoryID
INNER JOIN `tblTerritories` AS `t_c` ON t_c.territoryID = ct.territoryID
GROUP BY `v`.`videoID`, `c`.`categoryID`
edit4:
Если я переключусь с использования WHERE в качестве временного включения, то у меня все равно будет запрос, который занимает 0,98 секунды:
SELECT `c`.`categoryID`,
`cl`.`name` AS `category_name`,
`v`.*,
TRUE AS `categoried`,
GROUP_CONCAT(DISTINCT t_v.iso_3166_1_alpha_2) AS `video_territories`,
GROUP_CONCAT(DISTINCT t_c.iso_3166_1_alpha_2) AS `category_territories`,
`vl`.*,
GROUP_CONCAT(DISTINCT kl.name) AS `keywords`
FROM `tblCategories` AS `c`
INNER JOIN `tblCategoryLocalisedData` AS `cl` ON c.categoryID = cl.categoryID
LEFT JOIN `tblCategoryDurations` AS `cd` ON c.categoryID = cd.categoryID
LEFT JOIN `tblCategoryRules` AS `cr` ON c.categoryID = cr.categoryID
LEFT JOIN `tblCategoryVideos` AS `cv` ON c.categoryID = cv.categoryID
LEFT JOIN `tblVideos` AS `v` ON cv.videoID = v.videoID
LEFT JOIN `tblVideoTerritories` AS `vt` ON vt.videoID = v.videoID
LEFT JOIN `tblCategoryTerritories` AS `ct` ON ct.categoryID = c.categoryID
INNER JOIN `tblTerritories` AS `t_v` ON t_v.territoryID = vt.territoryID
INNER JOIN `tblTerritories` AS `t_c` ON t_c.territoryID = ct.territoryID
INNER JOIN `tblVideoLocalisedData` AS `vl` ON vl.videoID = v.videoID
LEFT JOIN `tblVideoKeywords` AS `vk` ON v.videoID = vk.videoID
LEFT JOIN `tblKeywords` AS `k` ON vk.keywordID = k.keywordID
LEFT JOIN `tblKeywordLocalisedData` AS `kl` ON kl.keywordID = k.keywordID
INNER JOIN `tblLanguages` AS `l` ON (l.languageID = cl.languageID OR cl.languageID IS NULL) AND (l.languageID = kl.languageID OR kl.languageID IS NULL) AND (l.languageID = vl.languageID OR vl.languageID IS NULL)
WHERE (cv.disabled IS NULL)
AND (cd.start_date < NOW() OR cd.start_date IS NULL)
AND (cd.end_date > NOW() OR cd.end_date IS NULL)
AND (cr.name IS NULL) AND (l.iso_639_1 = 'en')
GROUP BY `v`.`videoID`, `c`.`categoryID`
ORDER BY `c`.`categoryID` ASC
edit5:
Если я удаляю связанные с ключевым словом объединения, запрос происходит за 0,09 секунды ... Удаление tblKeyword и tblKeywordLocalisedData, но оставление tblVideoKeywords дает мне 0,80 секунды. Удаление tblVideoKeywords дает мне 0,09 секунды.
Но, похоже, у него есть индексы, поэтому я опять не понимаю:
CREATE TABLE `tblVideoKeywords` (
`videoKeywordID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`videoID` int(10) unsigned NOT NULL,
`keywordID` int(10) unsigned NOT NULL,
PRIMARY KEY (`videoKeywordID`),
KEY `fk_tblVideoKeywords_tblVideos1` (`videoID`),
KEY `fk_tblVideoKeywords_tblKeywords1` (`keywordID`),
CONSTRAINT `fk_tblVideoKeywords_tblKeywords1` FOREIGN KEY (`keywordID`) REFERENCES `tblKeywords` (`keywordID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_tblVideoKeywords_tblVideos1` FOREIGN KEY (`videoID`) REFERENCES `tblVideos` (`videoID`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
edit6:
Использование запроса от DRapp делает все намного быстрее. Объяснение его запроса теперь дает мне:
+----+-------------+---------+--------+--------------------------------------------------------------------------------------+-----------------------------------------+---------+------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+--------+--------------------------------------------------------------------------------------+-----------------------------------------+---------+------------------------+------+----------------------------------------------+
| 1 | SIMPLE | c | index | PRIMARY | PRIMARY | 4 | NULL | 3 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | cl | ALL | fk_tblCategoryLocalisedData_tblCategories1,fk_tblCategoryLocalisedData_tblLanguages1 | NULL | NULL | NULL | 5 | Using where; Using join buffer |
| 1 | SIMPLE | lang_cl | ALL | PRIMARY | NULL | NULL | NULL | 2 | Using where; Using join buffer |
| 1 | SIMPLE | cd | ref | fk_tblCategoryDurations_tblCategories | fk_tblCategoryDurations_tblCategories | 4 | db.c.categoryID | 1 | |
| 1 | SIMPLE | cr | ref | fk_tblCategoryRules_tblCategories1 | fk_tblCategoryRules_tblCategories1 | 4 | db.c.categoryID | 1 | Using where; Not exists |
| 1 | SIMPLE | cv | ALL | fk_tblCategoryVideos_tblCategories1,fk_tblCategoryVideos_tblVideos1 | NULL | NULL | NULL | 2 | Using where; Using join buffer |
| 1 | SIMPLE | ct | ALL | fk_tblCategoryTerritories_tblCategories1,fk_tblCategoryTerritories_tblTerritories1 | NULL | NULL | NULL | 2 | Using where; Using join buffer |
| 1 | SIMPLE | t_c | eq_ref | PRIMARY | PRIMARY | 4 | db.ct.territoryID | 1 | |
| 1 | SIMPLE | v | eq_ref | PRIMARY | PRIMARY | 4 | db.cv.videoID | 1 | Using where |
| 1 | SIMPLE | vt | ref | fk_tblVideoTerritories_tblVideos1,fk_tblVideoTerritories_tblTerritories1 | fk_tblVideoTerritories_tblVideos1 | 4 | db.v.videoID | 1 | Using where |
| 1 | SIMPLE | t_v | eq_ref | PRIMARY | PRIMARY | 4 | db.vt.territoryID | 1 | |
| 1 | SIMPLE | vl | ALL | fk_tblVideoLocalisedData_tblLanguages1,fk_tblVideoLocalisedData_tblVideos1 | NULL | NULL | NULL | 9 | Using where; Using join buffer |
| 1 | SIMPLE | lang_vl | eq_ref | PRIMARY | PRIMARY | 4 | db.vl.languageID | 1 | Using where |
| 1 | SIMPLE | vk | ALL | fk_tblVideoKeywords_tblVideos1,fk_tblVideoKeywords_tblKeywords1 | NULL | NULL | NULL | 15 | Using where; Using join buffer |
| 1 | SIMPLE | k | eq_ref | PRIMARY | PRIMARY | 4 | db.vk.keywordID | 1 | Using where; Using index |
| 1 | SIMPLE | kl | ref | fk_tblKeywordLocalisedData_tblKeywords1,fk_tblKeywordLocalisedData_tblLanguages1 | fk_tblKeywordLocalisedData_tblKeywords1 | 4 | db.k.keywordID | 1 | Using where |
| 1 | SIMPLE | lang_kl | eq_ref | PRIMARY | PRIMARY | 4 | db.kl.languageID | 1 | Using where |
+----+-------------+---------+--------+--------------------------------------------------------------------------------------+-----------------------------------------+---------+------------------------+------+----------------------------------------------+
17 rows in set (0.01 sec)