2009-07-29 6 views
1

У меня есть два запроса, которые помогут получить связанных тегов из базы данных mysql, один работает, в моей нет, мой вопрос: «почему?»Mysql Sub-Select Problem

Проблема: При выполнении первого запроса, сервер MySQL получает 100% использование центрального процессора, и должен быть перезапущен, чтобы работать снова.

запроса 1 (не работает):

SELECT tags.*, COUNT(ct.company_id) AS count 
FROM company2tag ct, tags 
WHERE ct.company_id IN (
    SELECT ct.company_id FROM company2tag ct 
    WHERE ct.tag_id = 18 
    GROUP BY ct.company_id 
    HAVING COUNT(ct.company_id) = 1 
) 
AND tags.id != 18 
AND tags.id = ct.tag_id 
GROUP BY ct.tag_id 
ORDER BY count DESC 
LIMIT 5; 

Запрос 2 (работы):

SELECT tags.*, COUNT(ct.company_id) AS count 
FROM company2tag ct, tags 
WHERE ct.company_id IN (5864, 5870, 6140, 6221, 6268) 
    AND tags.id != 18 
    AND tags.id = ct.tag_id 
GROUP BY ct.tag_id 
ORDER BY count DESC 
LIMIT 5; 

моему пониманию два запроса, приведенные выше, полностью совпадают, только разница в том, что первый запрос извлекает свой «company_id» через подзапрос.

Как это может произойти?

+0

И как этот подзапрос идет сам по себе? – paxdiablo

+0

работает отлично, он возвращает список company_id. – smoove

+0

Я не могу понять, как работает ваш второй запрос, потому что SQL недействителен. Все выбранные поля, которые не являются агрегатными функциями, должны присутствовать в списке GROUP BY (теги. * В вашем случае). – Tihauan

ответ

3

Прежде всего, у вас могут возникнуть проблемы из первого запроса, потому что у вас есть две таблицы с псевдонимом ct ... один во внешнем запросе, один в подзапросе.

Во-вторых, вы можете переписать как JOIN:

SELECT tags.*, COUNT(ct.company_id) AS count 
FROM company2tag ct 
INNER JOIN tags ON tags.id = ct.tag_id 
INNER JOIN (
    SELECT company_id FROM company2tag 
    WHERE tag_id = 18 
    GROUP BY company_id 
    HAVING COUNT(company_id) = 1 
) ctf ON ct.company_id = ctf.company_id 
WHERE tags.id != 18 
GROUP BY ct.tag_id 
ORDER BY count DESC 
LIMIT 5; 

Обратите внимание, что я на самом деле не проверял это.

+0

+1 для упоминания переписывания IN как СОЕДИНЕНИЯ. Предполагается, что IN должен соответствовать кратким спискам. Если у вас есть больший список, основанный на других таблицах/запросах, он должен быть JOIN. –

+0

Спасибо за это! Ваше решение примерно в 10 раз быстрее и не блокирует сервер. – smoove

2

MySQL не очень хорошо оптимизирует IN условия.

Условие в вашем первом запросе не может быть легко переписано как EXISTS, поэтому MySQL проверяет результаты для каждой строки.

Если вы хотите выбрать company_id «S, которые упоминаются более чем один раз в tag 18, то лучше переписать этот запрос, как, например:

SELECT tags.*, COUNT(company_id) AS count 
FROM company2tag ct 
JOIN tags 
ON  tags.id = ct.tag_id 
WHERE ct.tag_id <> 18 
     AND NOT EXISTS 
     (
     SELECT NULL 
     FROM company2tag cti 
     WHERE cti.tag_id = 18 
       AND cti.company_id = ct.company_id 
     LIMIT 1, 1 
     ) 
GROUP BY 
     ct.tag_id 
ORDER BY 
     count DESC 

Основная идея здесь заключается в том, что вам не нужно COUNT(*) : достаточно просто проверить, что существует как минимум два значения.

Смотрите эту статью в своем блоге для аналогичной задачи:

Имея следующий индекс:

CREATE INDEX ix_company2tag_tag_company_id ON company2tag (tag_id, company_id) 

позволит значительно улучшить этот запрос.