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

Елиминиране на присъединяване:Когато SQL Server премахва ненужните таблици

Гост автор:Берт Вагнер (@bertwagner)

Елиминирането на присъединяване е една от многото техники, които оптимизаторът на заявки на SQL Server използва за създаване на ефективни планове за заявки. По-конкретно, елиминирането на присъединяване се случва, когато SQL Server може да установи равенство чрез използване на логика на заявка или ограничения на надеждна база данни, за да елиминира ненужните присъединявания. Вижте пълната видео версия на тази публикация в моя канал в YouTube.

Присъединете се към елиминирането в действие

Най-простият начин да се обясни елиминирането на присъединяване е чрез серия от демонстрации. За тези примери ще използвам демонстрационната база данни WideWorldImporters.

За да започнем нещата, ще разгледаме как работи елиминирането на присъединяване, когато има външен ключ:

ИЗБЕРЕТЕ il.* FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;

В този пример връщаме данни само от Sales.InvoiceLines, където съответстващ InvoiceID се намира в Sales.Invoices. Въпреки че може да очаквате планът за изпълнение да покаже оператор за присъединяване в таблиците Sales.InvoiceLines и Sales.Invoices, SQL Server изобщо не си прави труда да разглежда Sales.Invoices:

SQL Server избягва присъединяването към таблицата Sales.Invoices, тъй като се доверява на референтната цялост, поддържана от ограничението на външния ключ, дефинирано в InvoiceID между Sales.InvoiceLines и Sales.Invoices; ако в Sales.InvoiceLines съществува ред, ред със съответстващата стойност за InvoiceID трябва съществуват в Sales.Invoices. И тъй като ние връщаме данни само от таблицата Sales.InvoiceLines, SQL Server изобщо не трябва да чете никакви страници от Sales.Invoices.

Можем да потвърдим, че SQL Server използва ограничението за външния ключ, за да елиминира присъединяването, като премахнем ограничението и изпълним отново нашата заявка:

ALTER TABLE [Sales].[InvoiceLines] DROP ОГРАНИЧЕНИЕ [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];

Без информация за връзката между нашите две таблици, SQL Server е принуден да извърши обединяване, сканирайки индекс в нашата таблица Sales.Invoices, за да намери съвпадащи идентификатори на фактури.

От гледна точка на I/O, SQL Server трябва да прочете допълнителни 124 страници от индекс в таблицата Sales.Invoices и това е само защото е в състояние да използва тесен (една колона) индекс, създаден от различно ограничение за външен ключ. Този сценарий може да се развие много по-лошо при по-големи таблици или таблици, които не са индексирани по подходящ начин.

Ограничения

Докато предишният пример показва основите на това как работи елиминирането на присъединяване, трябва да сме наясно с няколко предупреждения.

Първо, нека добавим обратно нашето ограничение за външен ключ:

ПРОМЕНЯТ ТАБЛИЦА [Продажби].[Редове на фактури] С ОГРАНИЧЕНИЕ ЗА ДОБАВЯНЕ НА NOCHECK [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] ВЪНШЕН КЛЮЧ([InvoiceID])РЕФЕРЕНЦИИ [Продажби].[Фактури] ([>InvoiceID]); 

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

Причината за това е, че когато отново добавихме нашето ограничение за външен ключ, SQL Server не знае дали междувременно са били променени някакви данни. Всички нови или променени данни може да не се придържат към това ограничение, така че SQL Server не може да се довери на валидността на нашите данни:

SELECT f.name AS external_key_name ,OBJECT_NAME(f.parent_object_id) AS table_name ,COL_NAME(fc.parent_object_id, fc.parent_column_id) AS constraint_column_name ,OBJECT_NAME (f.referenced_object_id,f.referenced_object_id,f.referenced_object_id,f.referenced_object_id,f.ref. ) КАТО referenced_column_name ,f.is_not_trustedFROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns КАТО fc ON f.object_id =fc.constraint_object_idWHERE f.parent_object_id =OBJECT_Invo('LiSanes'); 

За да възстановим доверието на SQL Server към това ограничение, трябва да проверим неговата валидност:

ПРОМЕНЯ ТАБЛИЦА [Продажби].[Редове за фактури] С ОГРАНИЧЕНИЕ НА ПРОВЕРКА [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];

При големи таблици тази операция може да отнеме известно време, да не говорим за режийните разходи на SQL Server за валидиране на тези данни по време на всяка промяна на вмъкване/актуализация/изтриване напред.

Друго ограничение е, че SQL Server не може да елиминира свързани таблици, когато заявката трябва да върне каквито и да е данни от тези потенциални кандидати за елиминиране:

ИЗБЕРЕТЕ il.*, i.InvoiceDateFROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;

Елиминирането на присъединяване не се случва в заявката по-горе, тъй като ние изискваме връщането на данни от Sales.Invoices, принуждавайки SQL Server да чете данни от тази таблица.

И накрая, важно е да се отбележи, че елиминирането на присъединяване няма да се случи, когато външният ключ има множество колони или ако таблиците са в tempdb. Последното е една от няколкото причини, поради които не трябва да се опитвате да решавате проблеми с оптимизацията, като копирате таблиците си в tempdb.

Допълнителни сценарии

Множество таблици

Елиминирането на присъединяване не е ограничено само до вътрешни свързвания на две таблици и таблици с ограничения на външния ключ.

Например, можем да създадем допълнителна таблица, която препраща към нашата колона Sales.Invoices.InvoiceID:

СЪЗДАВАНЕ НА ТАБЛИЦА Sales.InvoiceClickTracking ( InvoiceClickTrackingID bigint IDENTITY PRIMARY KEY, InvoiceID int -- други полета ще отидат тук ); GO ALTER TABLE [Продажби].[InvoiceClickTracking] С ПРОВЕРКА ДОБАВЯНЕ НА ОГРАНИЧЕНИЕ [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices] ВЪНШЕН КЛЮЧ([InvoiceID]) РЕФЕРЕНЦИИ [Продажби].[Фактури] ([InvoiceID]); 

Присъединяването на тази таблица към нашата оригинална примерна заявка също ще позволи на SQL Server да елиминира нашата таблица Sales.Invoices:

ИЗБЕРЕТЕ il.InvoiceID, ict.InvoiceID ОТ Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID INNER JOIN Sales.InvoiceClickTracking ict ON i.InvoiceID =ict.InvoiceID;
>

SQL Server може да елиминира таблицата Sales.Invoices поради преходната връзка между връзките на тези таблици.

Уникални ограничения

Вместо ограничение за външен ключ, SQL Server също ще извърши елиминиране на присъединяване, ако може да се довери на връзката с данни с уникално ограничение:

ALTER TABLE [Sales].[InvoiceClickTracking] DROP ОГРАНИЧЕНИЕ [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices]; GO ALTER TABLE Sales.InvoiceClickTracking ДОБАВИ ОГРАНИЧЕНИЕ UQ_InvoiceID UNIQUE (InvoiceID); ИЗБЕРЕТЕ i.InvoiceID ОТ Sales.InvoiceClickTracking ict НАДЯСНО ПРИСЪЕДИНЕТЕ Sales.Invoices i ON ict.InvoiceID =i.InvoiceID;

Външни свързвания

Докато SQL Server може да изведе ограничения на релациите, други типове обединения също могат да претърпят елиминиране на таблицата. Например:

ИЗБЕРЕТЕ il.InvoiceIDFROM Sales.InvoiceLines il LEFT JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID

Тъй като все още имаме нашето ограничение за външен ключ, което налага, че всеки InvoiceID в Sales.InvoiceLines трябва да има съответен InvoiceID в Sales.Invoices, SQL Server няма проблем да връща всичко от Sales.InvoiceLINEs, без да е необходимо да се присъединява към Sales.Invoices:

Не се изисква ограничение

Ако SQL Server може да гарантира, че няма да има нужда от данни от определена таблица, той потенциално може да елиминира присъединяване.

В тази заявка не се извършва елиминиране на присъединяване, тъй като SQL Server не може да идентифицира дали връзката между Sales.Invoices и Sales.InvoiceLines е 1 към 1, 1 към 0 или 1 към много. Той е принуден да прочете Sales.InvoiceLines, за да определи дали са открити съвпадащи редове:

ИЗБЕРЕТЕ i.InvoiceIDFROM Sales.InvoiceLines il RIGHT JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;

Въпреки това, ако посочим, че искаме DISTINCT набор от i.InvoiceIDs, всяка уникална стойност от Sales.Invoices се връща от SQL Server, независимо от това какво отношение имат тези редове с Sales.InvoiceLines.

-- Само за да докажем, че тук не се играе външен ключ ALTER TABLE [Sales].[InvoiceLines] DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];GO -- Нашият отличен набор от резултатиSELECT DISTINCT i.InvoiceIDFROM Sales.Invoice JOINs Sales il RIn i ON il.InvoiceID =i.InvoiceID;

Прегледи

Едно предимство на елиминирането на присъединяване е, че може да работи с изгледи, дори ако основната заявка за изглед не може да използва елиминиране на присъединяване:

-- Добавете обратно нашата FK ALTER TABLE [Sales].[InvoiceLines] С ПРОВЕРКА ДОБАВЯНЕ НА ОГРАНИЧЕНИЕ [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] ВЪНШЕН КЛЮЧ([InvoiceID])РЕФЕРЕНЦИИ [Продажби].[Идентификация на фактури] ([GO Създаване на фактури]); нашия изглед с помощта на заявка, която не може да използва елиминиране на присъединяванеCREATE VIEW Sales.vInvoicesAndInvoiceLinesAS SELECT i.InvoiceID, i.InvoiceDate, il.Quantity, il.TaxRate FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ICEIDil ON; GO -- Елиминирането на присъединяване работи, защото не избираме нито една колона от основната таблица Sales.Invoices SELECT Quantity, TaxRate FROM Sales.vInvoicesAndInvoiceLines;

Заключение

Елиминирането на присъединяване е оптимизация, която SQL Server извършва, когато прецени, че може да предостави точен набор от резултати, без да е необходимо да чете данни от всички таблици, посочени в изпратената заявка. Тази оптимизация може да осигури значителни подобрения на производителността чрез намаляване на броя на страниците, които SQL Server трябва да прочете, но често идва за сметка на необходимостта от поддържане на определени ограничения на базата данни. Можем да преработваме заявки, за да постигнем по-опростените планове за изпълнение, които предоставя елиминирането на присъединяване, но това, че оптимизаторът на заявки автоматично опростява нашите планове чрез премахване на ненужни обединявания е приятно предимство.

Отново ви каня да гледате пълната видео версия на тази публикация.

За автора

Бърт е разработчик на бизнес разузнаване от Кливланд, Охайо. Той обича да пише бързо изпълняващи се заявки и обича да помага на другите да се научат да бъдат самодостатъчни решаващи SQL проблеми. Берт блогове за SQL Server на bertwagner.com и създава видеоклипове на SQL Server в YouTube на youtube.com/c/bertwagner.
  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Има ли еквивалент на SELECT ... INTO OUTFILE в SQL Server Management Studio?

  2. Как да направя търсене, чувствително към малки и големи букви в клауза WHERE (използвам SQL Server)?

  3. Как да създадете пряк път за SSMS и да се свържете автоматично със SQL сървър по подразбиране - SQ:Server / TSQL Урок, част 4

  4. Как да съхранявам изображения в колона varbinary(max)?

  5. Разбиране на анализатора на работното натоварване за картографиране на тесните места в производителността