Синтаксис INNER JOIN, вложенный в OUTER JOIN против результатов запроса

10

TLDR; Если вы посмотрите на 2 плана выполнения, есть ли простой ответ, который лучше? Я специально не создавал индексы, чтобы было легче увидеть, что происходит.

В продолжение моего предыдущего вопроса, в котором мы обнаружили разницу в производительности запросов между различными стилями соединения (т. Е. Вложенный и традиционный), я понял, что вложенный синтаксис также изменяет поведение запроса. Рассмотрим следующие 2 запроса.

SELECT  a.*, m.*, n.*
FROM    dbo.Autos a
LEFT JOIN dbo.Models m
  JOIN dbo.Manufacturers n  -- <-- Nested INNER JOIN
  ON n.ManufacturerID = m.ManufacturerID
ON m.ModelID = a.ModelID

введите описание изображения здесь

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

введите описание изображения здесь

Используя традиционный синтаксис, мы должны изменить соединение для Manufactures на внешнее соединение, например, так ... но это меняет план запроса.

SELECT a.*, m.*, n.*
FROM dbo.Autos a
LEFT JOIN dbo.Models m
ON m.ModelID = a.ModelID
LEFT JOIN dbo.Manufacturers n -- <-- Now LEFT OUTER JOIN
ON n.ManufacturerID = m.ManufacturerID

введите описание изображения здесь

JeffInCO
источник

Ответы:

12

Если вы посмотрите на 2 плана выполнения, есть ли простой ответ, который лучше? Я специально не создавал индексы, чтобы было легче увидеть, что происходит.

Второй план имеет более низкую оценочную стоимость, поэтому в этом ограниченном смысле он «лучше».

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

Вторая форма запроса - это поиск плана с использованием только внешних вложений вложенных циклов в начале процесса поиска, и оптимизатор снова решает, что план достаточно хорош. Так получилось, что этот план оценивается дешевле.

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

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

«Вложенный синтаксис» является лишь одним аспектом всей спецификации синтаксиса соединения ANSI. Чтобы включить полную логическую спецификацию для более сложных шаблонов соединения, спецификация допускает (необязательные) круглые скобки и FROMподзапросы предложений.

Запрос может быть написан с использованием того же синтаксиса ANSI с использованием скобок:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN
(
    dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) ON M.ModelID = A.ModelID;

Эта форма ясно показывает , что логическое требование заключается в левое соединение с Autosв результате внутрипартийных присоединения Manufacturersк Models. Пропуск необязательных скобок дает форму, которую вы называете «вложенной»:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
    ON M.ModelID = A.ModelID;

Это не другой синтаксис - он просто пропускает необязательные скобки и немного переформатирует.

Как упоминал Мартин, в этом случае также возможно выразить логическое требование, используя внутренние объединения, за которыми следует правое внешнее соединение:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
RIGHT JOIN dbo.Autos AS A
    ON A.ModelID = M.ModelID;

Все три формы запроса, приведенные выше, используют один и тот же синтаксис соединения ANSI. Все три также создают один и тот же физический план выполнения с предоставленным набором данных:

Общий план выполнения

Как я уже упоминал в своем ответе на предыдущий вопрос, запросы, которые выражают одно и то же логическое требование, не всегда приводят к одному и тому же плану выполнения. Какую форму логического запроса вы предпочитаете использовать, во многом зависит от стиля. Нет никакой корреляции между одним конкретным стилем и «лучшими» планами запросов в целом. Я бы вообще не советовал переписывать запрос, чтобы получить конкретный план, если новый запрос не является действительно логически идентичным оригиналу.

Стандарт SQL также допускает FROMподзапросы предложений, поэтому еще один способ написать ту же спецификацию запроса:

SELECT * 
FROM dbo.Autos AS A
LEFT JOIN
(
    SELECT
        N.ManufacturerID,
        ManufacturerName = N.Name,
        M.ModelID,
        ModelName = M.Name
    FROM dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) AS R1
    ON R1.ModelID = A.ModelID;

Используя традиционный синтаксис, мы должны изменить соединение на «Производители» на внешнее объединение, например, так ... но это меняет план запроса.

Это , вероятно , изменяет значение запроса, в этом случае это технически не является действительной альтернативой (но см ypercube «s комментарий на ваш вопрос).

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

Пол Уайт 9
источник