Database
 sql >> база данни >  >> RDS >> Database

Цели на редове, част 3:Anti Joins

Тази публикация е част от поредица от статии за целите на редовете. Можете да намерите останалите части тук:

  • Част 1:Задаване и идентифициране на цели на редове
  • Част 2:Полусъединяване

Тази част обхваща кога и защо оптимизаторът въвежда цел на ред за анти присъединяване.

Въведение

Анти-съединяването е известно още като анти-полусъединяване. Връща всеки ред от вход A за свързване, за който няма съвпадение може да се намери на вход B.

За анти присъединяване:

  • Оптимизаторът може добавете цел на ред от вътрешната страна към прилагане (съединяване на корелирани вложени цикли) само анти присъединяване .
  • Цел на ред не е добавен за некорелирани вложени цикли против присъединяване, хеш анти присъединяване или сливане анти присъединяване.
  • Както винаги, всяка цел на ред се добавя само ако е по-ниска от прогнозата без приложена цел за ред.
  • Излишна вътрешна страна TOP клаузи и DISTINCT/GROUP BY операциите могат да бъдат опростени.

Разширявайки първия куршум по-горе, основната разлика между целите за прилагане на полусъединяване и прилагане на целите на ред срещу присъединяване е:

  • Прилагане на полусъединяване винаги включва цел на ред (стига да е по-малко от оценката без целта).
  • Едно прилагане против присъединяване може да включва цел на ред , но само ако логическото анти присъединяване се преобразува в приложение по време на оптимизация въз основа на разходите .

Извинявам се, че тези правила не са по-прости, но не съм ги създавал аз. Надяваме се, че някои дискусии и примери ще направят всичко по-ясно.

По подразбиране няма цел за ред срещу присъединяване

Оптимизаторът приема, че хората пишат полусъединяване (непряко, напр. с помощта на EXISTS ) с очакването, че търсеният ред ще бъде намерен . Зададена е цел за прилагане на полусъединяване ред от оптимизатора, за да помогне за бързото намиране на този очакван съвпадащ ред.

Заанти присъединяване (изразено например с помощта на NOT EXISTS ) предположението на оптимизатора е, че съответстващ ред няма да бъде намерен . Не е зададена цел за ред за прилагане срещу присъединяване от оптимизатора, защото очаква да трябва да провери всички редове, за да потвърди, че няма съвпадение.

Ако се окаже, че има съвпадащ ред, прилагането анти присъединяване може да отнеме повече време, за да намери този ред, отколкото ако се използва цел за ред. Независимо от това, анти присъединяването ще прекрати търсенето си веднага щом се срещне (неочаквано) съвпадение.

Прилагане на целта на реда против присъединяване

SQL Server не ни предоставя начин да напишем анти присъединяване директно, така че трябва да използваме заобиколни решения като NOT EXISTS , NOT IN/ANY/SOME , или EXCEPT . Всяка от тези форми води до представяне на подзаявка в анализираното дърво в началото на компилацията на заявката. Тази подзаявка винаги се развива в приложение и след това се трансформира в логическо анти присъединяване където е възможно (подробностите са същите като за полусъединяване, обсъдено в част 2). Всичко това се случва, преди да бъде обмислен дори тривиален план.

За да може анти присъединяване да получи цел на ред, то трябва да влезе оптимизация на базата на разходите като логическо анти присъединяване (което означава, че трансформацията от приложение по-горе трябва да е била успешна). След това базираният на разходите оптимизатор трябва да избере да приложи логическото анти присъединяване като прилагане . За да се случи това, оптимизаторът първо трябва да избере да проучва опцията за кандидатстване; след това трябва да избере това като най-евтиния вариант (за тази част от плана).

Цел за ред срещу присъединяване се задава от някое от правилата за оптимизация, базирани на разходите, които могат да трансформират присъединяване в приложение. Анти присъединяване, коетовлиза оптимизация на базата на разходи като приложение (тъй като преобразуването в логическо анти присъединяване е неуспешно) не има приложена цел за ред.

Оптимизаторът, базиран на разходите, ще проучи и избере опцията за присъединяване към прилагане само ако има ефективен начин за намиране на всички съвпадащи вътрешни странични редове (например с помощта на индекс). Оптимизаторът няма да проучи опцията, ако във вътрешната страна на поддървото за присъединяване липсва нещо полезно за предиката за прилагане, за да се „закопчава“. Това може да бъде индекс, временен индекс (чрез нетърпелив индексен пул) или друг логически ключ. Добавянето на целта за ред помага на оптимизатора да оцени цената на опцията за присъединяване към прилагане, като се има предвид, че трябва да бъде разположен максимум един ред.

Имайте предвид, че прилагането на анти присъединяване може да се появи в план за изпълнение без цел за ред. Това се случва, когато първоначалната трансформация от прилагане към присъединяване се провали, както е сравнително често. Когато това се случи, анти присъединяването започва да съществува в базирания на разходите оптимизатор като приложение и така никога не е добавена цел за ред от някое от правилата за присъединяване към прилагане.

Разбира се, цел за ред може да бъде въведена и от вътрешната страна на това приложение чрез различен механизъм (не е свързан с приложението), например от отделен оператор Top.

За да обобщим:

  • Анти присъединяването може да получи цел за ред само по време на оптимизация на базата на разходи (CBO).
  • Правилата, които превеждат анти присъединяване към приложение, добавят цел за ред.
  • Анти присъединяването трябва да влезе в CBO като присъединяване, а не като приложение.
  • За да въведете CBO като присъединяване, по-ранните фази трябва да могат да пренапишат подзаявката като присъединяване (чрез етап на прилагане).
  • CBO изследва само присъединяването, за да приложи трансформация в обещаващи случаи.

Пример

Малко по-трудно е да се демонстрира всичко това за прилагане на анти присъединяване, отколкото в случая за прилагане на полуприсъединяване. Причините за това ще бъдат разгледани в част 4.

Междувременно, ето един пример на AdventureWorks, показващ как възниква прилагане на анти присъединяване с цел на ред, като се използват същите недокументирани флагове за проследяване като при полусъединяване. Флаг за проследяване 8608 е добавен, за да покаже първоначалната структура на бележка в началото на оптимизация въз основа на разходите.

SELECT P.ProductID 
FROM Production.Product AS P
WHERE 
    NOT EXISTS 
    (
        SELECT 1
        FROM Production.TransactionHistoryArchive AS THA 
        WHERE THA.ProductID = P.ProductID
 
        UNION ALL
 
        SELECT 1
        FROM Production.TransactionHistory AS TH 
        WHERE TH.ProductID = P.ProductID
    )
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8607, QUERYTRACEON 8608, QUERYTRACEON 8612, QUERYTRACEON 8621);

Съществуващата подзаявка първо се трансформира в приложение: