Сравнение производительности запросов: объединение с выбором отличительного от таблицы - PullRequest
0 голосов
/ 13 октября 2019

У меня есть две таблицы человек и город . Таблица person и таблица city связаны с использованием city_id лично. Таблица person содержит около миллионов строк, а таблица city - около 10000 строк.

indexes on person: index1: id, index2: city_id
indexes on city:   index1: id

Мне нужно выбрать все те города, которыене иметь строки, связанной с человеком. Таблица city и person выглядит следующим образом (демонстрационные данные).

CITY                PERSON

id  city            id  name   city_id
-------------       ------------------
1    city-1         1   name-1   1
2    city-2         2   name-2   2
3    city-3         3   name-3   2
4    city-4         4   name-4   3
5    city-5         5   name-5   1
6    city-6         6   name-6   3
7    city-7         7   name-7   4
8    city-8         8   name-8   8

Я написал два запроса, чтобы получить результат:

query1:

     select c.id, c.city 
     from city c 
     left join person p on c.id = p.city_id  
     where p.id is null

query2:

     select * 
     from city 
     where id not in ( select distinct city_id from person)

план выполнения обоих запросов выглядит примерно так:

Для запроса 1: mysq для запроса 2: enter image description here

Затем я использовал профилирование и пару раз выполнил оба запроса, чтобы увидеть, сколько времени они занимают:

query1: 0.000729 0.000737 0.000763
query2: 0.000857 0.000840 0.000852

Очевидно, что запрос данных1 выше, чем запрос2.

Я запутался, поскольку то, что я понимаю, query2 должно превзойти query1 . Поскольку вложенный запрос query2 использует city_id, который проиндексирован, и mysql может использовать city_id index , чтобы получить все id , но query1 использует соединение, которое получит декартово произведение обеих таблиц. Это потому, что я использовал меньше данных f. человек (1000) и город (200) записей .

Чего мне не хватает из-за того, что query1 работает лучше, чем query2.

Редактировать

Из документации mysql:

covering index: An index that includes all the columns retrieved by a query. Instead of using 
the index values as pointers to find the full table rows, the query returns values 
from the index structure, saving disk I/O

Это было предположение, которое я сделал, когда пришел к запросу2.

Ответы [ 2 ]

2 голосов
/ 13 октября 2019

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

Соединения, как правило, дорогостоящие, я бы сказал.

2 голосов
/ 13 октября 2019

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

Я бы порекомендовал написать это как:

select c.* 
from city c
where not exists (select 1 from person p where p.city_id = c.id);

А для производительности вам нужен индекс наperson(city_id).

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

not in не совсем эквивалентен. Вот несколько причин:

  1. select distinct может сработать оптимизатором. В этом нет необходимости, но некоторые базы данных могут фактически работать по-разному.
  2. NULL s обрабатываются по-разному. Если любая строка в подзапросе возвращает значение NULL, тогда вообще не будет строк не будет возвращено из внешнего запроса.
...