Что быстрее: присоединиться к GROUP BY или подзапросу? - PullRequest
10 голосов
/ 01 июля 2010

Допустим, у нас есть две таблицы: 'Car' и 'Part', с соединительной таблицей в 'Car_Part'.Скажем, я хочу видеть все машины, в которых есть деталь 123.Я мог бы сделать это:

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
INNER JOIN Car_Part ON Car_Part.Car_Id = Car.Car_Id
WHERE Car_Part.Part_Id = @part_to_look_for
GROUP BY Car.Col1, Car.Col2, Car.Col3

Или я мог бы сделать это

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
WHERE Car.Car_Id IN (SELECT Car_Id FROM Car_Part WHERE Part_Id = @part_to_look_for)

Теперь все во мне хочет использовать первый метод, потому что меня воспитали хорошие родители, которыепривил мне пуританскую ненависть к подзапросам и любовь к теории множеств, но мне было предложено, чтобы выполнение этого большого GROUP BY было хуже, чем подзапрос.

Я должен отметить, что мына SQL Server 2008. Я должен также сказать, что в действительности я хочу выбрать на основе Идентификатор детали, Тип детали и, возможно, другие вещи.Итак, запрос, который я хочу сделать, на самом деле выглядит так:

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
INNER JOIN Car_Part ON Car_Part.Car_Id = Car.Car_Id
INNER JOIN Part ON Part.Part_Id = Car_Part.Part_Id
WHERE (@part_Id IS NULL OR Car_Part.Part_Id = @part_Id)
AND (@part_type IS NULL OR Part.Part_Type = @part_type)
GROUP BY Car.Col1, Car.Col2, Car.Col3

Или ...

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
WHERE (@part_Id IS NULL OR Car.Car_Id IN (
    SELECT Car_Id 
    FROM Car_Part 
    WHERE Part_Id = @part_Id))
AND (@part_type IS NULL OR Car.Car_Id IN (
    SELECT Car_Id
    FROM Car_Part
    INNER JOIN Part ON Part.Part_Id = Car_Part.Part_Id
    WHERE Part.Part_Type = @part_type))

Ответы [ 3 ]

4 голосов
/ 01 июля 2010

Лучшее, что вы можете сделать, - это протестировать их самостоятельно на реалистичных объемах данных.Это будет полезно не только для этого запроса, но и для всех будущих запросов, когда вы не уверены, какой из них лучший.

Важные вещи, которые необходимо сделать, включают:- проверка объемов данных уровня производства- проверять честно и последовательно (очистить кэш: http://www.adathedev.co.uk/2010/02/would-you-like-sql-cache-with-that.html)
- проверить план выполнения

Вы можете либо отслеживать с помощью SQL Profiler и проверять продолжительность / чтения / записи / ЦП, либо SET STATISTICS IO ON; SET STATISTICS TIME ON; длявыводить статистику в SSMS, а затем сравнивать статистику для каждого запроса.

Если вы не можете выполнить этот тип тестирования, вы будете потенциально подвергать себя проблемам с производительностью, что вам придется потомНастройте / исправьте. Существуют инструменты, которые вы можете использовать, которые будут генерировать данные для вас.

3 голосов
/ 01 июля 2010

У меня есть похожие данные, поэтому я проверил план выполнения для обоих стилей запроса.К моему удивлению, Column In Subquery (CIS) создал план выполнения с на 25% меньшими затратами на ввод-вывод по сравнению с запросом внутреннего соединения (IJ).В плане выполнения CIS я получаю 2 сканирования индекса промежуточной таблицы (Car_Part) по сравнению с просмотром индекса промежуточного и относительно более дорогого хеш-соединения в IJ.Мои индексы исправны, но не кластеризованы, поэтому понятно, что сканирование кластеров может быть выполнено немного быстрее.Я сомневаюсь, что это повлияет на стоимость хеш-соединения, которое является более дорогим шагом в запросе IJ.

Как указали другие, это зависит от ваших данных.Если вы работаете со многими гигабайтами в этих 3 таблицах, то отстранитесь. Если ваши строки пронумерованы сотнями или тысячами, то вы можете разделить волоски из-за очень небольшого прироста производительности. Я бы сказал, что запрос IJ гораздо более читабелен, если он достаточно хорош, делайте любыебудущий разработчик, который прикоснется к вашему коду и даст им что-нибудь более удобное для чтения.Количество строк в моих таблицах равно 188877, 283912, 13054, и оба запроса вернулись за меньшее время, чем потребовалось, чтобы выпить кофе.

Небольшой постскриптум: поскольку вы не агрегируете никаких числовых значений, похоже, что вы имеете в видувыбрать отличное.Если вы на самом деле не собираетесь что-то делать с группой, вам легче увидеть свое намерение с выбором отличного, а не сгруппированным в конце.Стоимость ввода-вывода такая же, но одно указывает на ваше намерение лучше, ИМХО.

2 голосов
/ 01 июля 2010

В SQL Server 2008 я ожидал бы, что In будет быстрее, поскольку это эквивалентно этому.

SELECT Car.Col1, Car.Col2, Car.Col3 
FROM Car
WHERE EXISTS(SELECT * FROM Car_Part
            WHERE Car_Part.Car_Id = Car.Car_Id
            AND Car_Part.Part_Id = @part_to_look_for
)

то есть нужно только проверить наличие строки, не присоединиться к ней, а затем удалить дубликаты,Это обсуждается здесь .

...