Лучший способ удовлетворить ваше любопытство по поводу такого рода вещей - запустить Management Studio и посмотреть на План выполнения. Вы также захотите посмотреть на SQL Profiler. Как сказал один из моих профессоров: «Компилятор является окончательным авторитетом». Подобный подход применяется, когда вы хотите узнать профиль производительности ваших запросов в SQL Server - просто посмотрите .
Начиная с этого момента, этот ответ был обновлен
Фактическое сравнение может быть очень показательным. Например, в тестировании, которое я только что выполнил, я обнаружил, что любой подход может дать самое быстрое время в зависимости от характера запроса. Например, запрос вида:
Select F1, F2, F3 From Table1 Where F4='X' And UID in (Select UID From Table2)
дал сканирование таблицы в Table1 и простое сканирование индекса в таблице 2, за которым последовало правое полусоединение.
Запрос вида:
Select A.F1, A.F2, A.F3 From Table1 A inner join Table2 B on (A.UID=B.UID)
Where A.Gender='M'
дал тот же план выполнения с одной оговоркой: на этот раз совпадение хешей было простым правым соединением. Итак, это первое, на что следует обратить внимание: планы выполнения существенно не отличались.
Однако это не повторяющиеся запросы, поскольку второй может возвращать несколько идентичных записей (по одной для каждой записи в таблице 2). Удивительным моментом была производительность: подзапрос был намного быстрее, чем внутреннее соединение. С наборами данных в младших тысячах (спасибо Red Data SQL Generator) внутреннее соединение было в 40 раз медленнее . Я был довольно ошеломлен.
Хорошо, а как насчет настоящих яблок на яблоки? Это совпадает с внутренним соединением - обратите внимание на дополнительный шаг для выявления дубликатов:
Select Distinct A.F1, A.F2, A.F3 From Table1 A inner join Table2 B
on (A.UID=B.UID)
Where A.Gender='M'
План выполнения действительно изменяется в том смысле, что существует дополнительный шаг - сортировка после внутреннего соединения. Как ни странно, время резко падает, так что два запроса почти идентичны (в двух из пяти испытаний внутреннее объединение происходит немного быстрее). Теперь я могу представить, что первое внутреннее объединение (без «отличного») несколько длиннее только из-за того, что в окно запроса пересылается больше данных - но оно было только в два раза больше (две записи Table2 для каждой записи Table1 ). У меня нет хорошего объяснения, почему первое внутреннее соединение было намного медленнее.
При добавлении предиката к поиску в таблице 2 с использованием подзапроса:
Select F1, F2, F3 From Table1 Where F4='X' And UID in
(Select UID From Table2 Where F1='Y')
затем сканирование индекса изменяется на сканирование кластерного индекса (что имеет смысл, поскольку поле UID имеет свой собственный индекс в таблицах, которые я использую), и процент времени, который он занимает, увеличивается. Также добавлена операция объединения потоков. Конечно же, это замедляет запрос. Однако, очевидно, что кеширование плана вступает в силу, так как первый запуск запроса показывает гораздо больший эффект, чем последующие.
Когда вы добавляете предикат, используя внутреннее соединение, весь план меняется довольно резко (оставлено читателю как упражнение - этот пост достаточно длинный). Производительность, опять же, почти такая же, как и у подзапроса - до тех пор, пока включен «Отличительный». Как и в первом примере, исключение отличных привело к значительному увеличению времени до завершения.
И последнее: кто-то предложил (и ваш вопрос теперь включает) запрос в форме:
Select Distinct F1, F2, F3 From table1, table2
Where (table1.UID=table2.UID) AND table1.F4='X' And table2.F1='Y'
План выполнения для этого запроса аналогичен плану внутреннего соединения (после сканирования исходной таблицы в table2 и объединения с объединением происходит сортировка, а не хеш-соединение двух таблиц). Производительность обоих также сопоставима. Возможно, мне понадобится больший набор данных, чтобы выявить разницу, но пока я не вижу никаких преимуществ для этой конструкции или конструкции "Exists".
С учетом всего сказанного - ваши результаты могут отличаться. Я не приблизился к тому, чтобы охватить весь спектр запросов, с которыми вы можете столкнуться, когда я выполнял вышеуказанные тесты. Как я уже говорил в начале, инструменты, включенные в SQL Server, являются вашими друзьями: используйте их.
Итак: зачем выбирать одно над другим? Это действительно сводится к вашим личным предпочтениям, поскольку, как представляется, нет никакого преимущества для внутреннего соединения с подзапросом с точки зрения сложности времени во всем диапазоне примеров, которые я тестирую.
В большинстве классических запросов я использую внутреннее соединение только потому, что «вырос» с ними. Я использую подзапросы, однако, в двух ситуациях. Во-первых, некоторые запросы проще понять с помощью подзапроса: связь между таблицами очевидна. Вторая и самая важная причина, однако, заключается в том, что я часто нахожусь в состоянии динамического генерирования SQL из моего приложения, и почти всегда легче автоматически генерировать подзапросы из кода.
Итак, вывод заключается в том, что лучшим решением является то, которое делает вашу разработку наиболее эффективной.