Улучшение запроса

Я пытаюсь выполнить простой sql-запрос:

SELECT DISTINCT id
FROM marketing
WHERE type = 'email'
 AND id NOT IN (
 SELECT id
 FROM marketing
 WHERE type = 'letter'
 )
ORDER BY id;

Для запуска требуется очень много времени, и я полагаю, что это связано с выбором в заявлении where (существует большое количество идентификаторов), но я не могу придумать способ его улучшить.

Во-первых, это может быть причиной того, что запрос настолько медленный, и во-вторых, любое предложение о том, как его улучшить?

Edit:

Система баз данных: MySql

Идентификатор индексируется, но не является первичным ключом в этой таблице; это внешний ключ.

4 ответа

Здесь альтернатива вашему запросу, хотя в соответствии с Quassnoi здесь (MySQL) он должен работать аналогичным образом.

select email.id
 from marketing email
left join marketing letter on letter.type='letter' and letter.id=email.id
 where email.type='email' and letter.id is null
 group by email.id
 order by email.id;

Три основных способа написания этого типа запроса НЕ ВХОДЯТ, НЕ СУЩЕСТВУЮТ (коррелирован) или LEFT JOIN/IS NULL. Quassnoi сравнивает их для MySQL (ссылка выше), SQL Server, Oracle и PostgreSQL.


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

select id from marketing m1
left outer join marketing m2 on m1.id = m2.id and m2.type = 'letter'
where m1.type = 'email' and m2.id IS NULL

Это приведет к тому, что все строки в маркетинге будут иметь тип "электронная почта", а идентификатор типа "письмо" не будет соответствовать. Если вы хотите другой набор, используйте IS NOT NULL. Правильный индекс в столбце id - это все, что вам нужно для максимальной скорости выполнения, с типом в качестве закрытого столбца.


select distinct id
from marketing a
where type = 'email'
and not exists (
 select 'X'
 from marketing b
 where a.id = b.id
 and type = 'letter' )
order by id


Вы также можете использовать этот запрос как запрос агрегации. Условия, которые вы ищете, это то, что id имеет по крайней мере одну строку, где type = 'email' и нет строк, где type = 'letter':

select id
from marketing m
group by id
having SUM(case when type = 'letter' then 1 else 0 end) = 0 and
 SUM(case when type = 'email' then 1 else 0 end) > 0

Возможно, этот запрос будет работать быстрее с индексом на marketing(id, type). order by id избыточен в MySQL, потому что group by выполняет упорядочение.

licensed under cc by-sa 3.0 with attribution.