Чи варто вкладати залежні зовнішні об'єднання в SQL Server?


9

Я чув про це неоднозначну інформацію і сподіваюся на канонічну чи експертну думку.

Якщо у мене декілька LEFT OUTER JOINs, кожен залежно від останнього, чи краще їх вкладати?

Для надуманого , наприклад, JOINдо MyParentзалежить від JOINдо MyChild: http://sqlfiddle.com/#!3/31022/5

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    MyChild AS c
        ON c.[Id] = gc.[ParentId]
LEFT OUTER JOIN
    MyParent AS p
        ON p.[id] = c.[ParentId]

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

Порівняно з http://sqlfiddle.com/#!3/31022/7

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    (
    MyChild AS c            
    LEFT OUTER JOIN
        MyParent AS p
            ON p.[id] = c.[ParentId]
    )
    ON c.[Id] = gc.[ParentId]

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

Як показано вище, вони створюють різні плани запитів у SS2k8


Мені подобається використовувати вкладені приєднання: michaeljswart.com/2012/09/when-i-use-nested-joins Це може бути питанням стилю.
Michael J Swart

@MichaelJSwart ваш блог з'являється тільки обговорити , коли залежне JOINцеINNER JOIN
Matthew

1
Як ви хочете визначити "краще"? Особисто мені здається, що перше набагато простіше читати - мій розум не підстрибує, намагаючись змінити відносини. Маючи ON ... ONдва рази поспіль (дужки чи ні) дуже заплутаним.
Аарон Бертран

4
Коли я не знаходжу різниці в ефективності між двома способами щось робити, наступне питання, яке я задаю собі: якщо я потрапив автобусом або виграв в лотерею сьогодні, яку версію було б найлегше зрозуміти і підтримувати той, хто завтра захопить мій код ?
Аарон Бертран

1
use planНатяк працює при пересадці другого плану запиту на перший , але не навпаки.
Мартін Сміт

Відповіді:


3

Це абсолютно не канонічна відповідь, але я помітив, що для вкладених планів запитів циклів, показаних у скрипті SQL, можна застосувати план із запиту 2 до запиту 1 з використанням USE PLANпідказки, але спроба зворотної операції не вдається

Процесор запиту не зміг скласти план запитів, оскільки підказка USE PLAN містить план, який не можна було перевірити як законний для запиту. Видаліть або замініть ПІДКЛЮЧЕННЯ ПЛАНУ. Для найкращої ймовірності успішного форсування плану переконайтеся, що план, наданий у підказці USE PLAN, є генерованим автоматично SQL сервером для того ж запиту.

Вимкнення правила перетворення оптимізатора ReorderLOJN запобігає успіху попереднього успішного натяку на план.

Експерименти з більшою кількістю даних показують, що SQL Server, безумовно, здатний перетворитись (A LOJ B) LOJ Cі A LOJ (B LOJ C)природним шляхом, але я не побачив жодних доказів того, що зворотне дійсне.

Дуже надуманий випадок, коли перший запит працює краще, ніж другий

DROP TABLE  MyGrandChild , MyChild,  MyParent

CREATE TABLE MyParent
(Id int)

CREATE TABLE MyChild
(Id int PRIMARY KEY
,ParentId int,
Filler char(8000) NULL)

CREATE TABLE MyGrandChild
(Id int
,ParentId int)

INSERT INTO MyChild
                      (Id, ParentId)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY @@SPID),
                     ROW_NUMBER() OVER (ORDER BY @@SPID)    
FROM master..spt_values  v1, master..spt_values                  

INSERT INTO MyGrandChild
                      (Id, ParentId)
OUTPUT INSERTED.Id INTO MyParent
SELECT TOP (3000) Id, Id AS ParentId
FROM MyChild
ORDER BY Id

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN MyChild AS c
         ON c.[Id] = gc.[ParentId]
       LEFT OUTER JOIN MyParent AS p
         ON p.[Id] = c.[ParentId]

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN( MyChild AS c
                        LEFT OUTER JOIN MyParent AS p
                          ON p.[Id] = c.[ParentId])
         ON c.[Id] = gc.[ParentId] 

Що дає плани

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

Для мене для запиту 1 минув час 108 мс проти 1163 мс для запиту 2.

Запит 1

Table 'Worktable'. Scan count 0, logical reads 0 
Table 'MyChild'. Scan count 0, logical reads 9196
Table 'MyGrandChild'. Scan count 1, logical reads 7
Table 'MyParent'. Scan count 1, logical reads 5

Запит 2

Table 'MyParent'. Scan count 1, logical reads 15000
Table 'MyChild'. Scan count 0, logical reads 9000 
Table 'MyGrandChild'. Scan count 1, logical reads 7

Таким чином, може бути попередньо припущено, що перший ("непропущений") синтаксис є потенційно вигідним, оскільки дозволяє розглянути більше потенційних замовлень на приєднання, але я не зробив достатньо вичерпних тестів, щоб мати велику впевненість у цьому як загальне правило.

Можливо, цілком можливо придумати зустрічні приклади, коли запит 2 працює краще. Спробуйте обидва і подивіться плани виконання.


-1

не існує такого типу ПРИЄДНАННЯ під назвою "Вкладене приєднання". це ще один варіант написання ПРИЄДНАННЯ може бути зручним для читання. ви можете бачити їх як "Підзапроси" лише для розуміння мети.

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

І якщо ви стурбовані виконанням запиту, а підказка "Приєднайтесь до замовлення" не використовується в запиті, то не має значення, чи запит написано з "Введене приєднання" або Усі "Зовнішнє приєднання". SQL-сервер придумав порядок на основі вартості об'єднання двох таблиць та / або результату. SQL Server одночасно з'єднується між двома наборами даних.

насправді, уявіть, що по-другому "вкладене з'єднання", якщо SQL-сервер вирішить виконати другу частину, "MyChild AS c ЛИВНІЙ ВІДПОЛУЧАЙТЕ ПРИЄДНАЙТЕСЬ до MyParent AS p ON p. [id] = c. [ParentId]" і ці таблиці трапляються мати рядки, які потрібно відкинути в НАСТУПНІЙ ЛІВНІЙ ПРИЄДНАТИ у такому випадку SQL-сервер витратив непотрібні ресурси на те, щоб виконувати ЗОВНІШНІ ПРИЄДНАННЯ цих двох і передаючи результат на наступний ПРИЄДНАЙТЕ

ви також можете подивитися подібне запитання і відповісти відповідне тут. Розуміння синтаксису "Вкладене приєднання"


1
Чому ж тоді вони створюють різні плани запитів, не використовуючи FORCE JOIN ORDERпідказки?
Матвій

без підказки, ми не можемо гарантувати приєднання до замовлення, і як ви бачите інший план виконання, який це підтверджує. наприклад, першим способом "використання всіх зовнішніх приєднань" SQL-сервер може робити будь-який із цих двох. спочатку "MyChild + MyGrandChild", а потім приєднуйтесь до "MyParent". Або спочатку "MyChild + MyParent", а потім приєднуйтесь до "MyGrandChild".
Ануп Шах
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.