2010-07-01 1 views
9

Предположим, у нас есть две таблицы: «Автомобиль» и «Часть», с соединительной таблицей в «Car_Part». Скажем, я хочу видеть все автомобили, в которых есть часть 123. Я мог бы сделать это:Что происходит быстрее: присоединяйтесь к GROUP BY или подзапрос?

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)) 
+2

Вы запустили оба? Посмотрел планы запросов? Обозначил это? – Oded

+1

Мне пришлось бы генерировать нагрузку данных, поэтому я не буду обходить ее до следующей недели. И когда я искал Google для ответа, я не нашел его, поэтому стоит задать вопрос онлайн для всех, кто может смотреть. – d4nt

+0

Group By is трудоемкий, используемый для вычисления таких вещей, как средние, суммы и т. Д. Вы, кажется, используете его для устранения дубликатов. Попробуйте DISTINCT без группы ... – Alocyte

ответ

3

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

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

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

4

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

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

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

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

2

С 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 
) 

i.e.Есть только проверить на наличие строки, а не объединить ее, а затем удалить дубликаты. Это discussed here.