Существует старая статья 2009 года , которую я видел связанной с stackoverflow много раз. Тест показывает, что запрос NOT EXISTS
на 27% (на самом деле это 26%) медленнее, чем два других запроса (LEFT JOIN
и NOT IN
).
Однако оптимизатор был улучшен от версии к версии. И идеальный оптимизатор создаст один и тот же план выполнения для всех трех запросов. Но пока оптимизатор не совершенен, ответ на вопрос "Какой запрос быстрее?" может зависеть от фактической настройки (которая включает версию, настройки и данные).
В прошлом я проводил подобные тесты, и все, что я помню, это то, что LEFT JOIN
никогда не был значительно медленнее, чем любой другой метод. Но из любопытства я только что создал новый тест для MariaDB 10.3.13 портативной версии Windows с настройками по умолчанию.
Фиктивные данные:
set @parents = 1000;
drop table if exists parent;
create table parent(
parent_id mediumint unsigned primary key
);
insert into parent(parent_id)
select seq
from seq_1_to_1000000
where seq <= @parents
;
drop table if exists child;
create table child(
child_id mediumint unsigned primary key,
parent_id mediumint unsigned not null,
index (parent_id)
);
insert into child(child_id, parent_id)
select seq as child_id
, floor(rand(1)*@parents)+1 as parent_id
from seq_1_to_1000000
;
НЕ В:
set @start = TIME(SYSDATE(6));
select count(*) into @cnt
from parent p
where p.parent_id not in (select parent_id from child c);
select @cnt, TIMEDIFF(TIME(SYSDATE(6)), @start);
LEFT JOIN:
set @start = TIME(SYSDATE(6));
select count(*) into @cnt
from parent p
left join child c on c.parent_id = p.parent_id
where c.parent_id is null;
select @cnt, TIMEDIFF(TIME(SYSDATE(6)), @start);
НЕ СУЩЕСТВУЕТ:
set @start = TIME(SYSDATE(6));
select count(*) into @cnt
from parent p
where not exists (
select *
from child c
where c.parent_id = p.parent_id
);
select @cnt, TIMEDIFF(TIME(SYSDATE(6)), @start);
Время выполнения в миллисекундах:
@parents | 1000 | 10000 | 100000 | 1000000
-----------|------|-------|--------|--------
NOT IN | 21 | 38 | 175 | 4459
LEFT JOIN | 24 | 40 | 183 | 1508
NOT EXISTS | 26 | 44 | 180 | 4463
Я выполнил запросы несколько раз и принял наименьшее значение времени. И SYSDATE
, вероятно, не лучший метод для измерения времени выполнения - так что не принимайте эти цифры как точные. Тем не менее, мы видим, что до 100K родительских строк разница невелика, а метод NOT IN
немного быстрее. Но с 1М родительскими строками LEFT JOIN
в три раза быстрее.
Заключение
Так, каков ответ? Я мог бы просто сказать: «LEFT JOIN» побеждает. Но правда в том, что этот тест ничего не доказывает. И ответ (так много раз): «Это зависит». Когда производительность имеет значение, лучшее, что вы можете сделать, это запустить свои собственные тесты с реальными запросами к реальным данным. Если у вас нет реальных данных (пока), вам следует создать фиктивные данные с объемом и распределением, которые вы ожидаете получить в будущем.