Когда использовать EXCEPT вместо NOT EXISTS в Transact SQL? - PullRequest
37 голосов
/ 02 ноября 2009

Я только недавно узнал о существовании нового предложения "EXCEPT" в SQL Server (немного поздно, я знаю ...) благодаря чтению кода, написанного коллегой. Это действительно поразило меня!

Но тогда у меня есть несколько вопросов относительно его использования: когда его рекомендуется использовать? Есть ли разница с точки зрения производительности между его использованием и коррелированным запросом, использующим "И НЕ СУЩЕСТВУЕТ ..."?

После прочтения статьи EXCEPT на BOL я подумал, что это просто сокращение для второго варианта, но был удивлен, когда переписал пару запросов, используя его (поэтому у них был синтаксис «И НЕ СУЩЕСТВУЕТ», гораздо более знакомый мне) а потом проверил планы выполнения - сюрприз! Версия EXCEPT имела более короткий план выполнения и выполнялась также быстрее. Это всегда так?

Итак, я хотел бы знать: каковы рекомендации по использованию этого мощного инструмента?

Ответы [ 5 ]

36 голосов
/ 03 ноября 2009

EXCEPT рассматривает NULL значения как совпадающие.

Этот запрос:

WITH    q (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  1
        ),
        p (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  2
        )
SELECT  *
FROM    q
WHERE   value NOT IN
        (
        SELECT  value
        FROM    p
        )

вернет пустой набор строк.

Этот запрос:

WITH    q (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  1
        ),
        p (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  2
        )
SELECT  *
FROM    q
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    p
        WHERE   p.value = q.value
        )

вернет

NULL
1

, а этот:

WITH    q (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  1
        ),
        p (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  2
        )
SELECT  *
FROM    q
EXCEPT
SELECT  *
FROM    p

вернет:

1

Рекурсивная ссылка также допускается в предложении EXCEPT в рекурсивном CTE, хотя она ведет себя странным образом: она возвращает все, кроме последней строки предыдущего набора, не все, кроме весь предыдущий набор:

WITH    q (value) AS
        (
        SELECT  1
        UNION ALL
        SELECT  2
        UNION ALL
        SELECT  3
        ),
        rec (value) AS
        (
        SELECT  value
        FROM    q
        UNION ALL
        SELECT  *
        FROM    (
                SELECT  value
                FROM    q
                EXCEPT
                SELECT  value
                FROM    rec
                ) q2
        )
SELECT  TOP 10 *
FROM    rec

---
1
2
3
-- original set
1
2
-- everything except the last row of the previous set, that is 3
1
3
-- everything except the last row of the previous set, that is 2
1
2
-- everything except the last row of the previous set, that is 3, etc.
1

SQL Server разработчики, должно быть, просто забыли запретить это.

8 голосов
/ 15 сентября 2012

Я провел большой анализ, за ​​исключением того, что не существует, не вошел и оставил внешнее соединение. Обычно левое внешнее соединение является самым быстрым для поиска пропущенных строк, особенно для соединения по первичному ключу. Not In может быть очень быстрым, если вы знаете, что это будет небольшой список, возвращаемый в select.

Я часто использую EXCEPT для сравнения того, что возвращается при переписывании кода. Запустите старый код, сохранив результаты. Запустите новый код, сохраняющий результаты, а затем используйте его, чтобы зафиксировать все различия. Это очень быстрый и простой способ найти различия, особенно когда нужно получить все различия, включая ноль. Очень хорошо для легкого кодирования на лету.

Но все ситуации разные. Я говорю каждому разработчику, которого я когда-либо обучал. Попытайся. Делайте тайминги по-разному. Попробуй, пора, сделай.

3 голосов
/ 03 ноября 2009

EXCEPT сравнивает все (парные) столбцы двух полных выборов. NOT EXISTS сравнивает две или более таблиц в соответствии с условиями, указанными в предложении WHERE в подзапросе после ключевого слова NOT EXISTS.

КРОМЕ можно переписать с помощью NOT EXISTS. (КРОМЕ ВСЕХ можно переписать с помощью ROW_NUMBER и НЕ СУЩЕСТВУЕТ.)

Получил это от здесь

2 голосов
/ 25 марта 2014

Если ваш запрос точно настроен, то нет никакой разницы в производительности по ч / б с использованием предложения EXCEPT и NOT EXIST / NOT IN .. впервые, когда я запустил EXCEPT после изменения в нем коррелированного запроса .. Я был удивлен, потому что возвращается с результатом всего за 7 секунд, в то время как коррелированный запрос возвращался через 22 секунды ... затем я использовал отдельное предложение в своем коррелированном запросе и перезапускаю .. он также вернулся через 7 секунд .. так что ИСКЛЮЧИТЬ хорошо, когда вы не знаете или у вас нет времени на точную настройку вашего запроса, в противном случае производительность будет одинаковой.

2 голосов
/ 03 ноября 2009

Не учитывается план выполнения SQL-сервера. Когда я сталкивался с проблемами производительности, я всегда обнаруживал, что это было совершенно произвольно (с точки зрения пользователя, я уверен, что авторы алгоритмов поймут почему), когда один синтаксис создавал план выполнения лучше, чем другой.

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

...