Медленный MySQL соединяется с внутренним выбором - PullRequest
0 голосов
/ 12 августа 2011

Следующий запрос работает, но становится медленнее, так как размер таблицы sendlog со временем увеличивается.Цель состоит в том, чтобы выбрать список всех подписчиков из таблицы newsletter_subscribeers, которые не имеют запись электронной почты для данного идентификатора бюллетеня в таблице newsletter_sendlog.В настоящее время это занимает около 2,2 секунд на моем сервере MySQL с только пару тысяч записей в sendlog.

SELECT `newsletter_subscribers`.* 
FROM `newsletter_subscribers`
    INNER JOIN `newsletter_to_subscriber` 
        ON newsletter_to_subscriber.subscriber_id = newsletter_subscribers.id
    LEFT JOIN (
        SELECT `newsletter_sendlog`.`subscriber_email` 
        FROM `newsletter_sendlog` 
        WHERE (newsletter_id='7')
      ) AS `sendlog` 
        ON newsletter_subscribers.email = sendlog.subscriber_email 
WHERE (sendlog.subscriber_email IS NULL) 
AND (newsletter_to_subscriber.newsletter_id = '7')

EXPLAIN (запрос) выводит следующее:

Output of EXPAIN

Я не слишком знаком с выводом EXPLAIN,но если я прочитал его правильно, это может означать, что он не использует индекс, который я определил в newsletter_sendlog.subscriber_email.Я пытался использовать USE INDEX (электронную почту) для этой таблицы, но, похоже, это не сработало.

Есть предложения, как это оптимизировать?Или, возможно, предложить другой запрос, который делает то же самое?


Создать таблицу для newsletter_sendlog:

CREATE TABLE `newsletter_sendlog` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `subscriber_email` varchar(100) NOT NULL default '',
  `newsletter_id` int(11) default NULL,
  `sendstatus` int(11) default NULL,
  `senddate` timestamp NOT NULL default CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  KEY `newsletter_id` (`newsletter_id`),
  KEY `email` (`subscriber_email`)
) ENGINE=MyISAM AUTO_INCREMENT=2933 DEFAULT CHARSET=latin1;

Создать таблицу для newsletter_subscribeers:

CREATE TABLE `newsletter_subscribers` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `email` varchar(100) NOT NULL default '',
  `name` tinytext,
  PRIMARY KEY  (`id`),
  KEY `email` (`email`)
) ENGINE=MyISAM AUTO_INCREMENT=2964 DEFAULT CHARSET=utf8;

Создать таблицу для newsletter_to_subscriber:

CREATE TABLE `newsletter_to_subscriber` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `newsletter_id` int(11) NOT NULL,
  `subscriber_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `newsletter_subscriber` (`newsletter_id`,`subscriber_id`)
) ENGINE=MyISAM AUTO_INCREMENT=2964 DEFAULT CHARSET=latin1;

Обновление:

Создание таблицы для newsletter_to_subscriber теперь выглядит следующим образом после добавления индекса для subscriber_id:

CREATE TABLE `newsletter_to_subscriber` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `newsletter_id` int(11) NOT NULL,
  `subscriber_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `newsletter_subscriber` (`newsletter_id`,`subscriber_id`),
  KEY `subscriber` (`subscriber_id`)
) ENGINE=MyISAM AUTO_INCREMENT=2964 DEFAULT CHARSET=latin1;

Объяснение запроса, предложенного @nobody:

Explain for nobody's query

Ответы [ 4 ]

1 голос
/ 12 августа 2011

Всегда лучше выбирать конкретные поля вместо звездочки (*) и избегать обратных кавычек (`). Попробуйте проверить, работает ли следующий (переписанный) запрос лучше:

SELECT 
    newsletter_subscribers.id,
    newsletter_subscribers.email,
    newsletter_subscribers.name
FROM
    newsletter_subscribers
    LEFT JOIN
        newsletter_to_subscriber
        ON
            newsletter_to_subscriber.subscriber_id = newsletter_subscribers.id
    LEFT JOIN
        newsletter_sendlog
        ON
            newsletter_subscribers.email = newsletter_sendlog.subscriber_email
WHERE
    newsletter_to_subscriber.newsletter_id = 7
    AND
        newsletter_sendlog.newsletter_id = 7
    AND
        newsletter_sendlog.subscriber_email IS NULL
0 голосов
/ 12 августа 2011

Во-первых, вам не нужен этот подзапрос:

SELECT `newsletter_subscribers`.* 
FROM `newsletter_subscribers`
    INNER JOIN `newsletter_to_subscriber` 
        ON( newsletter_to_subscriber.subscriber_id = newsletter_subscribers.id )
    LEFT JOIN `newsletter_sendlog`
        ON( newsletter_subscribers.email = newsletter_sendlog.subscriber_email AND
            newsletter_sendlog.newsletter_id = '7' )
WHERE newsletter_sendlog.subscriber_email IS NULL

запрос выше сделает работу.

Во-вторых, в newsletter_to_subscriber у вас есть индекс из нескольких частей для newsletter_id и subscriber_id, который не может быть использован вашим запросом, потому что он будет искать subscriber_id и занимает второе место в индексе. нужно иметь отдельный индекс для subscriber_id:

INDEX( subscriber_id )
0 голосов
/ 12 августа 2011

Не совсем уверен, но я думаю, что индекс игнорируется, потому что вы ищете значения NULL.

Существует другой, надеюсь, более эффективный способ выполнения этого запроса:

select *
from newsletter_subscribers
where email not in 
(select subscriber_email
from newsletter_sendlog
where newsletter_id='7')
0 голосов
/ 12 августа 2011
SELECT `newsletter_subscribers`.* FROM `newsletter_subscribers`
  INNER JOIN `newsletter_to_subscriber` 
    ON newsletter_to_subscriber.subscriber_id = newsletter_subscribers.id
  LEFT JOIN (
      SELECT `newsletter_sendlog`.`subscriber_email` FROM `newsletter_sendlog` 
        WHERE (newsletter_id='7')) AS `sendlog` 
    ON newsletter_subscribers.email=sendlog.subscriber_email 
  WHERE (sendlog.subscriber_email IS NULL) 
    AND (newsletter_to_subscriber.newsletter_id = '7')

Не могли бы вы попробовать реализовать индексный ключ для одного столбца newsletter_to_subscriber.subscriber_id

и посмотрите, поможет ли это?

Попробуйте использовать структуру таблицы, как показано ниже:

CREATE TABLE `newsletter_to_subscriber` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `newsletter_id` int(11) NOT NULL,
  `subscriber_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `newsletter_subscriber` (`newsletter_id`,`subscriber_id`)
  KEY `subscriber_id_key` (`subscriber_id`)
  KEY `newsletter_id_key` (`newsletter_id`)
) ENGINE=MyISAM AUTO_INCREMENT=2964 DEFAULT CHARSET=latin1;
...