Gästförfattare:Bert Wagner (@bertwagner)
Anslutningseliminering är en av många tekniker som SQL Server-frågeoptimeraren använder för att skapa effektiva frågeplaner. Närmare bestämt inträffar eliminering av join när SQL Server kan skapa likhet genom att använda frågelogik eller betrodda databasrestriktioner för att eliminera onödiga sammanfogningar. Se en fullständig videoversion av det här inlägget på min YouTube-kanal.
Gå med i Elimination In Action
Det enklaste sättet att förklara eliminering av medlemskap är genom en serie demos. För dessa exempel kommer jag att använda WideWorldImporters demodatabas.
Till att börja med tittar vi på hur eliminering av anslutning fungerar när en främmande nyckel är närvarande:
VÄLJ il.* FRÅN Sales.InvoiceLines il INNER JOIN Sales.Invoices i PÅ il.InvoiceID =i.InvoiceID;
I det här exemplet returnerar vi endast data från Sales.InvoiceLines där ett matchande InvoiceID finns i Sales.Invoices. Även om du kanske förväntar dig att exekveringsplanen visar en joinoperator i tabellerna Sales.InvoiceLines och Sales.Invoices, bryr SQL Server sig aldrig om att titta på Sales.Invoices alls:
SQL Server undviker att gå med i tabellen Sales.Invoices eftersom den litar på referensintegriteten som upprätthålls av den främmande nyckelbegränsningen som definieras på InvoiceID mellan Sales.InvoiceLines och Sales.Invoices; om det finns en rad i Sales.InvoiceLines, måste en rad med det matchande värdet för InvoiceID finns i Sales.Invoices. Och eftersom vi bara returnerar data från Sales.InvoiceLines-tabellen behöver SQL Server inte läsa några sidor från Sales.Invoices alls.
Vi kan verifiera att SQL Server använder den främmande nyckeln för att eliminera anslutningen genom att släppa begränsningen och köra vår fråga igen:
ÄNDRA TABELL [Försäljning].[Fakturader] SLUTA BEGRÄNSNING [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];
Utan information om förhållandet mellan våra två tabeller, tvingas SQL Server att utföra en koppling, genom att skanna ett index på vår Sales.Invoices-tabell för att hitta matchande InvoiceIDs.
Ur en I/O-synpunkt måste SQL Server läsa ytterligare 124 sidor från ett index i tabellen Sales.Invoices, och det är bara för att den kan använda ett smalt (en kolumn) index som skapats av en annan främmande nyckelrestriktion. Det här scenariot kan bli mycket värre på större bord eller bord som inte är korrekt indexerade.
Begränsningar
Även om det föregående exemplet visar grunderna för hur eliminering av medlemskap fungerar, måste vi vara medvetna om några varningar.
Låt oss först lägga till vår begränsning av främmande nyckel:
ÄNDRA TABELL [Försäljning].[Fakturalinjer] MED NOCK ADD CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] UTLÄNDSK NYCKEL([Faktura-ID])REFERENSER [Försäljning].[Fakturor] ([Faktura-ID]);
Om vi kör vår exempelfråga igen kommer vi att märka att vi inte får en plan som visar att anslutning elimineras; istället får vi en plan som skannar båda våra sammanfogade tabeller.
Anledningen till att detta inträffar är att SQL Server inte vet om någon data har ändrats under tiden när vi lade till vår främmande nyckel-begränsning. Alla nya eller ändrade data kanske inte följer denna begränsning, så SQL Server kan inte lita på giltigheten av våra data:
SELECT f.name AS outside_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) AS ) AS referenced_column_name ,f.is_not_trustedFROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns AS fc ON f.object_id =fc.constraint_object_idWHERE f.parent_object_id =OBJECT_ID(>'Lines'.Invopree');För att återupprätta SQL Servers förtroende för denna begränsning måste vi kontrollera dess giltighet:
ÄNDRA TABELL [Försäljning].[InvoiceLines] MED KONTROLLBEGRÄNSNING [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];På stora tabeller kan den här åtgärden ta lite tid, för att inte tala om överkostnaderna för SQL Server som validerar dessa data under varje infogning/uppdatering/borttagning av ändringar framöver.
En annan begränsning är att SQL Server inte kan eliminera sammanfogade tabeller när frågan behöver returnera data från dessa potentiella elimineringskandidater:
SELECT il.*, i.InvoiceDateFROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Anslutningseliminering sker inte i frågan ovan eftersom vi begär att data från Sales.Invoices returneras, vilket tvingar SQL Server att läsa data från den tabellen.
Slutligen är det viktigt att notera att join-eliminering inte kommer att inträffa när den främmande nyckeln har flera kolumner, eller om tabellerna är i tempdb. Det senare är en av flera anledningar till att du inte bör försöka lösa optimeringsproblem genom att kopiera dina tabeller till tempdb.
Ytterligare scenarier
Flera tabeller
Eliminering av kopplingar är inte bara begränsad till inre kopplingar med två tabeller och tabeller med begränsningar för främmande nyckel.
Vi kan till exempel skapa en extra tabell som refererar till vår kolumn Sales.Invoices.InvoiceID:
SKAPA TABELL Sales.InvoiceClickTracking ( InvoiceClickTrackingID bigint IDENTITET PRIMÄRNYCKEL, InvoiceID int -- andra fält skulle gå hit ); GÅ ÄNDRA TABELL [Försäljning].[InvoiceClickTracking] MED KONTROLL ADD CONSTRAINT [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices] UTLÄNDSK NYCKEL([InvoiceID]) REFERENSER [Försäljning].[Fakturor] ([Faktura-ID]);Om du ansluter den här tabellen till vår ursprungliga exempelfråga kommer även SQL Server att kunna eliminera vår Sales.Invoices-tabell:
VÄLJ il.InvoiceID, ict.InvoiceID FRÅN Sales.InvoiceLines il INNER JOIN Sales.Invoices i PÅ il.InvoiceID =i.InvoiceID INNER JOIN Sales.InvoiceClickTracking ict PÅ i.InvoiceID =ict.InvoiceID;
SQL Server kan eliminera tabellen Sales.Invoices på grund av den transitiva kopplingen mellan dessa tabellers relationer.
Unika begränsningar
Istället för en främmande nyckel-restriktion kommer SQL Server också att utföra join-eliminering om den kan lita på datarelationen med en unik restriktion:
ÄNDRA TABELL [Försäljning].[InvoiceClickTracking] SLOPP BEGRÄNSNING [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices]; GÅ ÄNDRA TABELL Sales.InvoiceClickTracking ADD CONSTRAINT UQ_InvoiceID UNIQUE (InvoiceID); GÅ VÄLJ i.InvoiceID FRÅN Sales.InvoiceClickTracking ict RIGHT JOIN Sales.Invoices i ON ict.InvoiceID =i.InvoiceID;
Ytteranslutningar
Så länge som SQL Server kan sluta sig till relationsbegränsningar kan även andra typer av kopplingar uppleva tabelleliminering. Till exempel:
VÄLJ il.InvoiceIDFROM Sales.InvoiceLines il LEFT JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceIDEftersom vi fortfarande har vår utlandsnyckel som tvingar fram att varje faktura-ID i Sales.InvoiceLines måste ha ett motsvarande faktura-ID i Sales.Invoices, har SQL Server inga problem att returnera allt från Sales.InvoiceLines utan att behöva gå med i Sales.Invoices:
Ingen begränsning krävs
Om SQL Server kan garantera att den inte behöver data från en viss tabell, kan den potentiellt eliminera en anslutning.
Ingen kopplingseliminering sker i den här frågan eftersom SQL Server inte kan identifiera om förhållandet mellan Sales.Invoices och Sales.InvoiceLines är 1-till-1, 1-till-0 eller 1-till-många. Den tvingas läsa Sales.InvoiceLines för att avgöra om några matchande rader hittas:
VÄLJ i.InvoiceIDFROM Sales.InvoiceLines il RIGHT JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Men om vi anger att vi vill ha en DISTINKT uppsättning av i.InvoiceIDs, returneras varje unikt värde från Sales.Invoices från SQL Server oavsett vilken relation dessa rader har med Sales.InvoiceLines.
-- Bara för att bevisa att ingen främmande nyckel är på spel här ALTER TABLE [Försäljning].[InvoiceLines] DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];GO -- Vårt distinkta resultatuppsättningSELECT DISTINCT i.InvoiceIDFROM Sales.InvoiceLines Sales.InvoiceLines. i PÅ il.InvoiceID =i.InvoiceID;
Visningar
En fördel med eliminering av anslutning är att den kan fungera med vyer, även om den underliggande vyfrågan inte kan använda eliminering av koppling:
-- Lägg till tillbaka vår FK ALTER TABELL [Försäljning].[Fakturalinjer] MED KONTROLL ADD BEGRÄNSNING [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] UTLÄNDSK NYCKEL([FakturaID])REFERENSER [Försäljning].[Fakturor] ([GO -- Skapa ID]); vår vy använder en fråga som inte kan använda join eliminationCREATE VIEW Sales.vInvoicesAndInvoiceLinesAS SELECT i.InvoiceID, i.InvoiceDate, il.Quantity, il.TaxRate FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON =il.InvoiceID; GO -- Eliminering av medlemskap fungerar eftersom vi inte väljer några -- kolumner från den underliggande tabellen Sales.Invoices SELECT Quantity, TaxRate FROM Sales.vInvoicesAndInvoiceLines;
Slutsats
Join-eliminering är en optimering som SQL Server utför när den fastställer att den kan ge en korrekt resultatuppsättning utan att behöva läsa data från alla tabeller som anges i den skickade frågan. Denna optimering kan ge betydande prestandaförbättringar genom att minska antalet sidor som SQL Server måste läsa, men det sker ofta på bekostnad av att behöva underhålla vissa databasbegränsningar. Vi kan omstrukturera frågor för att uppnå de enklare exekveringsplaner som join-eliminering ger, men att ha frågeoptimeraren automatiskt förenkla våra planer genom att ta bort onödiga joins är en trevlig fördel.
Återigen, jag inbjuder dig att titta på den fullständiga videoversionen av det här inlägget.
Om författaren
Bert är en business intelligence-utvecklare från Cleveland, Ohio. Han älskar att skriva snabba frågor och tycker om att hjälpa andra att lära sig att vara självförsörjande med SQL-problemlösare. Bert bloggar om SQL Server på bertwagner.com och skapar SQL Server YouTube-videor på youtube.com/c/bertwagner.