MySQL - объединение таблиц в одной и той же таблице несколько раз с разными условиями занимает вечно - PullRequest
3 голосов
/ 03 марта 2012

Упрощенно, у меня есть четыре таблицы.

ref_TagGroup (top-level descriptive containers for various tags)
ref_Tag (tags with name and unique tagIDs)
ref_Product
ref_TagMap (TagID,Container,ContainerType)
A fifth table, ref_ProductFamily exists but is not directly part of this query.

Я использую таблицу ref_TagMap для сопоставления тегов с продуктами, а также для сопоставления тегов с группами тегов, а также с семействами продуктов.Для параметра ContainerType установлено значение PROD / TAGGROUP / PRODFAM соответственно.

Итак, я хочу вернуть группу тегов, имя тега и количество продуктов И семейств продуктов, которым сопоставлен тег ... так что результат будет следующим:

GroupName |TagName |TagHitCnt

Мой вопрос: почему первый запрос возвращается в миллисекундах, второй запрос возвращается в миллисекундах, а не в третьем (это просто добавление условия «ИЛИ» для включения тега в продукт и тегана семейные сопоставления) занимает вечность (ну, в любом случае, более десяти минут ... Я не давал ей работать всю ночь.)

QUERY 1:

SELECT ref_taggroup.groupname,ref_tag.tagname,COUNT(DISTINCT IFNULL(ref_product.familyid,ref_product.id + 100000000),ref_product.name) AS 'taghitcnt' 
FROM (ref_taggroup,ref_tag,ref_product) 
LEFT JOIN ref_tagmap GROUPMAP ON GROUPMAP.containerid=ref_taggroup.groupid 
LEFT JOIN ref_tagmap PRODMAP ON PRODMAP.containerid=ref_product.id 
WHERE 
GROUPMAP.tagid=ref_tag.tagid AND GROUPMAP.containertype='TAGGROUP' 
AND
PRODMAP.tagid=ref_tag.tagid AND PRODMAP.containertype='PROD' 
GROUP BY tagname 
ORDER BY groupname,tagname ;

QUERY 2:

SELECT ref_taggroup.groupname,ref_tag.tagname,COUNT(DISTINCT IFNULL(ref_product.familyid,ref_product.id + 100000000),ref_product.name) AS 'taghitcnt' 
FROM (ref_taggroup,ref_tag,ref_product) 
LEFT JOIN ref_tagmap GROUPMAP ON GROUPMAP.containerid=ref_taggroup.groupid 
LEFT JOIN ref_tagmap PRODFAMMAP ON PRODFAMMAP.containerid=ref_product.familyid 
WHERE 
GROUPMAP.tagid=ref_tag.tagid AND GROUPMAP.containertype='TAGGROUP' 
AND
PRODFAMMAP.tagid=ref_tag.tagid AND PRODFAMMAP.containertype='PRODFAM' 
GROUP BY tagname 
ORDER BY groupname,tagname ;

QUERY 3:

SELECT ref_taggroup.groupname,ref_tag.tagname,COUNT(DISTINCT IFNULL(ref_product.familyid,ref_product.id + 100000000),ref_product.name) AS 'taghitcnt' 
FROM (ref_taggroup,ref_tag,ref_product) 
LEFT JOIN ref_tagmap GROUPMAP ON GROUPMAP.containerid=ref_taggroup.groupid 
JOIN ref_tagmap PRODMAP ON PRODMAP.containerid=ref_product.id 
JOIN ref_tagmap PRODFAMMAP ON PRODFAMMAP.containerid=ref_product.familyid 
WHERE 
GROUPMAP.tagid=ref_tag.tagid AND GROUPMAP.containertype='TAGGROUP' 
AND
((PRODMAP.tagid=ref_tag.tagid AND PRODMAP.containertype='PROD') 
OR 
(PRODFAMMAP.tagid=ref_tag.tagid AND PRODFAMMAP.containertype='PRODFAM' ))
GROUP BY tagname 
ORDER BY groupname,tagname ;

- Чтобы ответить на вопрос, который может возникнуть, в отчете COUNT Distinct ifnull предназначен для возврата одной записи для большого числа продуктов, которыесгруппированы по семьям и по одной записи для каждого «отдельного» продукта, которого нет в семье.Этот код хорошо работает в других запросах.

Я пытался выполнить UNION для первых двух запросов, и это работает и возвращается очень быстро, но это не практично по другим причинам, в которые я не буду вдаватьсяздесь.

Каков наилучший способ сделать это?Что я делаю не так?

Спасибо!


ДОБАВЛЕНИЕ ОБЪЯСНЕНИЯ ВЫХОДА

QUERY1                                  
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  GROUPMAP    ALL                 5640    Using where; Using temporary; Using filesort
1   SIMPLE  ref_tag ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.tagid    1   Using index
1   SIMPLE  ref_taggroup    ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.containerid  3   Using index
1   SIMPLE  PRODMAP ALL                 5640    Using where; Using join buffer
1   SIMPLE  ref_product eq_ref  PRIMARY PRIMARY 4   lsslave01.PRODMAP.containerid   1   

QUERY2                                  
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  GROUPMAP    ALL                 5640    Using where; Using temporary; Using filesort
1   SIMPLE  ref_tag ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.tagid    1   Using index
1   SIMPLE  ref_taggroup    ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.containerid  3   Using index
1   SIMPLE  PRODFAMMAP  ALL                 5640    Using where; Using join buffer
1   SIMPLE  ref_product ref FixtureType FixtureType 5   lsslave01.PRODFAMMAP.containerid    39  Using where

QUERY3                                  
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  GROUPMAP    ALL                 5640    Using where; Using temporary; Using filesort
1   SIMPLE  ref_tag ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.tagid    1   Using index
1   SIMPLE  ref_taggroup    ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.containerid  3   Using index
1   SIMPLE  PRODMAP ALL                 5640    Using join buffer
1   SIMPLE  PRODFAMMAP  ALL                 5640    Using where; Using join buffer
1   SIMPLE  ref_product eq_ref  PRIMARY,FixtureType PRIMARY 4   lsslave01.PRODMAP.containerid   1   Using where

enter code here

Еще одно обновление для всех, кто интересуется: я наконец-то позволилтретий запрос выше выполняется до завершения.Это заняло около 1000 секунд.Разделив это время на время выполнения каждого из запросов (1 или 2), мы получим число около 6000 ... что очень близко к размеру таблицы ref_tagmap, которую мы используем в нашей среде разработки (намного больше в производстве).Итак, похоже, что мы выполняем один запрос к каждой записи в этой таблице ... но я до сих пор не понимаю, почему.

Любая помощь будет высоко ценится ... и я имею в виду серьезно, серьезнооценили.

1 Ответ

0 голосов
/ 09 мая 2012

Это менее "ответ", чем пара замечаний / предложений.

Во-первых, мне интересно, могли бы вы использовать GROUP BY для целочисленного идентификатора вместо имени тега? Я бы изменил поле ref_TagMap.containertype для хранения перечисляемых значений tinyint, представляющих три возможных значения TAGGROUP, PROD и PRODFAM. Индексированное поле tinyint должно быть немного быстрее, чем индекс строковых значений. Это, вероятно, не будет иметь большого значения, потому что это второе условие в предложении соединения, и в любом случае в индексированных значениях не так много различий.

Далее следует наблюдение / напоминание о том, что когда первая половина оператора OR часто оценивается как FALSE, вы заставляете MySQL каждый раз оценивать обе половины условного выражения. Таким образом, вы хотите, чтобы условие, вероятнее всего, сначала оценивалось в ИСТИНА (то есть до ИЛИ).

Я сомневаюсь, что любая из этих двух проблем является вашей реальной проблемой ... хотя проблема во втором абзаце может сыграть свою роль. Похоже, что самый быстрый путь к производительной версии запроса 3 может заключаться в том, чтобы просто заполнить временную таблицу результатами первых двух запросов и извлечь из этой временной таблицы результаты, которые вы ищете третий. Возможно, при этом вы поймете, почему этот третий запрос такой медленный.

...