Вопрос о подзапросе оптимизации SQL Server против соединения - PullRequest
2 голосов
/ 02 марта 2010

Какой из этих запросов более эффективен, и будет ли современная СУБД (например, SQL Server) вносить изменения под капот, чтобы сделать их равными?

SELECT DISTINCT S# 
  FROM shipments 
 WHERE P# IN (SELECT P# 
                FROM parts 
               WHERE color = ‘Red’)

против

SELECT DISTINCT S# 
  FROM shipments, parts 
 WHERE shipments.P# = parts.P# 
   AND parts.color = ‘Red’

Ответы [ 4 ]

6 голосов
/ 02 марта 2010

Лучший способ удовлетворить ваше любопытство по поводу такого рода вещей - запустить 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 из моего приложения, и почти всегда легче автоматически генерировать подзапросы из кода.

Итак, вывод заключается в том, что лучшим решением является то, которое делает вашу разработку наиболее эффективной.

2 голосов
/ 02 марта 2010

Использование IN более читабельно, и я рекомендую использовать ANSI-92 поверх синтаксиса объединения ANSI-89:

SELECT DISTINCT S#
  FROM SHIPMENTS s
  JOIN PARTS p ON p.p# = s.p#
              AND p.color = 'Red'

Проверьте свои планы объяснения, чтобы увидеть, что лучше, потому что это зависит от данных и настроек таблицы.

2 голосов
/ 02 марта 2010

Если вы ничего не выбираете из таблицы, я бы использовал предложение EXISTS.

SELECT DISTINCT S# 
FROM shipments a
WHERE EXISTS (SELECT 1
              FROM parts b
              WHERE b.color = ‘Red’
                AND a.P# = b.P#)

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

1 голос
/ 02 марта 2010
SELECT DISTINCT S# 
FROM shipments,parts 
WHERE shipments.P# = parts.P# and parts.color = ‘Red’;

Использование IN вынуждает SQL Server не использовать индексацию для этого столбца, а подзапросы обычно медленнее

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...