MySQL подзапрос резко замедляется, но они работают нормально независимо

8

Запрос 1:

select distinct email from mybigtable where account_id=345

занимает 0,1 с

Запрос 2:

Select count(*) as total from mybigtable where account_id=123 and email IN (<include all from above result>)

занимает 0,2 с

Запрос 3:

Select count(*) as total from mybigtable where account_id=123 and email IN (select distinct email from mybigtable where account_id=345)

занимает 22 минуты и 90% в состоянии «подготовка». Почему это занимает так много времени.

Таблица является innodb с 3,2 млн строк на MySQL 5.0

Стьюи
источник

Ответы:

8

В Query 3 вы в основном выполняете подзапрос для каждой строки mybigtable против себя.

Чтобы этого избежать, нужно внести два основных изменения:

ОСНОВНОЕ ИЗМЕНЕНИЕ № 1: Рефакторинг запроса

Вот ваш оригинальный запрос

Select count(*) as total from mybigtable
where account_id=123 and email IN
(select distinct email from mybigtable where account_id=345)

Вы могли бы попробовать

select count(*) EmailCount from
(
    select tbl123.email from
    (select email from mybigtable where account_id=123) tbl123
    INNER JOIN
    (select distinct email from mybigtable where account_id=345) tbl345
    using (email)
) A;

или, может быть, количество на электронную почту

select email,count(*) EmailCount from
(
    select tbl123.email from
    (select email from mybigtable where account_id=123) tbl123
    INNER JOIN
    (select distinct email from mybigtable where account_id=345) tbl345
    using (email)
) A group by email;

ОСНОВНОЕ ИЗМЕНЕНИЕ № 2: Правильная индексация

Я думаю, у вас уже есть, так как Query 1 и Query 2 работают быстро. Убедитесь, что у вас есть составной индекс (account_id, email). Сделайте SHOW CREATE TABLE mybigtable\Gи убедитесь, что у вас есть. Если у вас его нет или вы не уверены, то все равно создайте индекс:

ALTER TABLE mybigtable ADD INDEX account_id_email_ndx (account_id,email);

ОБНОВЛЕНИЕ 2012-03-07 13:26 EST

Если вы хотите сделать NOT IN (), измените на INNER JOINa LEFT JOINи проверьте, что правая сторона имеет значение NULL, например:

select count(*) EmailCount from
(
    select tbl123.email from
    (select email from mybigtable where account_id=123) tbl123
    LEFT JOIN
    (select distinct email from mybigtable where account_id=345) tbl345
    using (email)
    WHERE tbl345.email IS NULL
) A;

ОБНОВЛЕНИЕ 2012-03-07 14:13 EST

Пожалуйста, прочитайте эти две ссылки на участие

Вот отличное видео на YouTube, где я научился проводить рефакторинг запросов и книгу, на которой он основан

RolandoMySQLDBA
источник
9

В MySQL подвыборы в предложении IN повторно выполняются для каждой строки во внешнем запросе, создавая, таким образом, O (n ^ 2). Короче говоря, не используйте IN (SELECT).

Аарон Браун
источник
1
  1. У вас есть индекс на account_id?

  2. Вторая проблема может быть связана с вложенными подзапросами, которые имеют ужасную производительность в 5.0.

  3. GROUP BY с предложением has быстрее, чем DISTINCT.

  4. Что вы пытаетесь сделать, что может быть лучше сделано через объединения в дополнение к пункту № 3?

Стивен Сенкомаго Мусоке
источник
1

При обработке подзапроса IN (), такого как ваш, требуется много обработки. Вы можете прочитать больше об этом здесь .

Моим первым предложением было бы попытаться переписать подзапрос в JOIN. Что-то вроде (не проверено):

SELECT COUNT(*) AS total FROM mybigtable AS t1
 INNER JOIN 
   (SELECT DISTINCT email FROM mybigtable WHERE account_id=345) AS t2 
   ON t2.email=t1.email
WHERE account_id=123
Дерек Дауни
источник