Синтаксис 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, який НЕ в таблиці Моделі.

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

Використовуючи традиційний синтаксис, ми маємо змінити приєднання для Виробництва до зовнішнього з'єднання, як так ... але це змінює план запитів.

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

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

Відповіді:


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 до вашого питання).

(Необов’язкові) дужки в синтаксисі приєднання ANSI існують саме для таких складних вимог приєднання, як ця, тому не варто боятися використовувати їх там, де це необхідно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.