Gastauteur:Bert Wagner (@bertwagner)
Het elimineren van joins is een van de vele technieken die de SQL Server-queryoptimalisatie gebruikt om efficiënte queryplannen te maken. Met name het verwijderen van joins vindt plaats wanneer SQL Server gelijkheid tot stand kan brengen door querylogica of vertrouwde databasebeperkingen te gebruiken om onnodige joins te elimineren. Bekijk een volledige videoversie van dit bericht op mijn YouTube-kanaal.
Doe mee aan Eliminatie in actie
De eenvoudigste manier om de eliminatie van joins uit te leggen, is door middel van een reeks demo's. Voor deze voorbeelden gebruik ik de demodatabase van WideWorldImporters.
Om te beginnen, zullen we kijken hoe het verwijderen van joins werkt wanneer een externe sleutel aanwezig is:
SELECTEER il.* FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
In dit voorbeeld retourneren we alleen gegevens van Sales.InvoiceLines waar een overeenkomende InvoiceID wordt gevonden in Sales.Invoices. Hoewel je zou verwachten dat het uitvoeringsplan een join-operator toont in de tabellen Sales.InvoiceLines en Sales.Invoices, neemt SQL Server nooit de moeite om naar Sales.Invoices te kijken:
SQL Server vermijdt deelname aan de tabel Sales.Invoices omdat het vertrouwt op de referentiële integriteit die wordt onderhouden door de externe sleutelbeperking die is gedefinieerd op InvoiceID tussen Sales.InvoiceLines en Sales.Invoices; als er een rij bestaat in Sales.InvoiceLines, een rij met de overeenkomende waarde voor InvoiceID moet bestaan in Sales.Invoices. En aangezien we alleen gegevens uit de tabel Sales.InvoiceLines retourneren, hoeft SQL Server helemaal geen pagina's uit Sales.Invoices te lezen.
We kunnen verifiëren dat SQL Server de externe-sleutelbeperking gebruikt om de join te elimineren door de beperking te laten vallen en onze query opnieuw uit te voeren:
ALTER TABLE [Sales].[InvoiceLines] DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];
Zonder informatie over de relatie tussen onze twee tabellen, wordt SQL Server gedwongen een join uit te voeren, waarbij een index wordt gescand op onze Sales.Invoices-tabel om overeenkomende InvoiceID's te vinden.
Vanuit een I/O-standpunt moet SQL Server 124 extra pagina's van een index in de tabel Sales.Invoices lezen, en dat is alleen omdat het een smalle index (met één kolom) kan gebruiken die is gemaakt door een andere beperking van de refererende sleutel. Dit scenario kan veel slechter uitpakken op grotere tafels of tafels die niet op de juiste manier zijn geïndexeerd.
Beperkingen
Hoewel het vorige voorbeeld de basis laat zien van hoe het verwijderen van joins werkt, moeten we ons bewust zijn van een paar kanttekeningen.
Laten we eerst onze externe sleutelbeperking toevoegen:
WIJZIG TABEL [Sales].[InvoiceLines] MET NOCHECK ADD CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] FOREIGN KEY([InvoiceID])REFERENTIES [Sales].[Facturen] ([InvoiceID]);
Als we onze voorbeeldquery opnieuw uitvoeren, zullen we merken dat we geen plan krijgen dat eliminatie van join vertoont; in plaats daarvan krijgen we een plan dat onze beide samengevoegde tabellen scant.
De reden dat dit gebeurt, is omdat SQL Server niet weet of er in de tussentijd gegevens zijn gewijzigd wanneer we onze externe-sleutelbeperking opnieuw hebben toegevoegd. Nieuwe of gewijzigde gegevens voldoen mogelijk niet aan deze beperking, dus SQL Server kan de geldigheid van onze gegevens niet vertrouwen:
SELECT f.name AS foreign_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) AS referenced_object ,COLnci ) 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('Sales');Lines
Om het vertrouwen van SQL Server in deze beperking te herstellen, moeten we de geldigheid ervan controleren:
WIJZIG TABEL [Sales].[InvoiceLines] MET CONTROLEER CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];Bij grote tabellen kan deze bewerking enige tijd duren, om nog maar te zwijgen van de overhead van SQL Server die deze gegevens valideert tijdens elke toekomstige wijziging van invoegen/bijwerken/verwijderen.
Een andere beperking is dat SQL Server samengevoegde tabellen niet kan elimineren wanneer de query gegevens van die potentiële verwijderingskandidaten moet retourneren:
SELECT il.*, i.InvoiceDateFROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Eliminatie van joins vindt niet plaats in de bovenstaande query omdat we vragen om gegevens van Sales.Invoices te retourneren, waardoor SQL Server wordt gedwongen gegevens uit die tabel te lezen.
Ten slotte is het belangrijk op te merken dat het verwijderen van joins niet plaatsvindt wanneer de refererende sleutel meerdere kolommen heeft of als de tabellen in tempdb staan. Dit laatste is een van de redenen waarom u optimalisatieproblemen niet moet proberen op te lossen door uw tabellen naar tempdb te kopiëren.
Aanvullende scenario's
Meerdere tabellen
Het elimineren van joins is niet alleen beperkt tot inner joins met twee tabellen en tabellen met beperkingen voor externe sleutels.
We kunnen bijvoorbeeld een extra tabel maken die verwijst naar onze kolom Sales.Invoices.InvoiceID:
MAAK TABEL Sales.InvoiceClickTracking (InvoiceClickTrackingID bigint IDENTITY PRIMARY KEY, InvoiceID int -- andere velden zouden hier komen); GA WIJZIG TABEL [Sales].[InvoiceClickTracking] MET CONTROLE ADD CONSTRAINT [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices] FOREIGN KEY([InvoiceID]) REFERENTIES [Sales].[Facturen] ([InvoiceID]);Door deze tabel aan onze oorspronkelijke voorbeeldquery toe te voegen, kan SQL Server ook onze tabel Sales.Invoices elimineren:
SELECTEER il.InvoiceID, ict.InvoiceID FROM 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 kan de tabel Sales.Invoices elimineren vanwege de transitieve associatie tussen de relaties van deze tabellen.
Unieke beperkingen
In plaats van een externe-sleutelbeperking, zal SQL Server ook de eliminatie van joins uitvoeren als het de gegevensrelatie kan vertrouwen met een unieke beperking:
ALTER TABLE [Sales].[InvoiceClickTracking] DROP CONSTRAINT [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices]; GA WIJZIG TABEL Sales.InvoiceClickTracking ADD CONSTRAINT UQ_InvoiceID UNIEK (InvoiceID); GA SELECTEER i.InvoiceID UIT Sales.InvoiceClickTracking ict RECHTS WORD LID VAN Sales.Invoices i ON ict.InvoiceID =i.InvoiceID;
Outer Joins
Zolang SQL Server relatiebeperkingen kan afleiden, kunnen ook andere typen joins tabelverwijdering ervaren. Bijvoorbeeld:
SELECT il.InvoiceIDFROM Sales.InvoiceLines il LEFT JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceIDAangezien we nog steeds onze externe sleutelbeperking hebben die afdwingt dat elke InvoiceID in Sales.InvoiceLines een overeenkomstige InvoiceID in Sales.Invoices moet hebben, heeft SQL Server geen probleem om alles van Sales.InvoiceLInes terug te sturen zonder dat u zich bij Sales.Invoices hoeft aan te sluiten:
Geen beperking vereist
Als SQL Server kan garanderen dat het geen gegevens uit een bepaalde tabel nodig heeft, kan het mogelijk een join elimineren.
Er vindt geen verwijdering van joins plaats in deze query omdat SQL Server niet kan identificeren of de relatie tussen Sales.Invoices en Sales.InvoiceLines 1-op-1, 1-op-0 of 1-op-veel is. Het wordt gedwongen om Sales.InvoiceLines te lezen om te bepalen of er overeenkomende rijen zijn gevonden:
SELECTEER i.InvoiceIDFROM Sales.InvoiceLines il RIGHT JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Als we echter specificeren dat we een DISTINCT set van i.InvoiceID's willen, keert elke unieke waarde van Sales.Invoices terug van SQL Server, ongeacht welke relatie die rijen hebben met Sales.InvoiceLines.
-- Gewoon om te bewijzen dat hier geen externe sleutel in het spel is ALTER TABLE [Sales].[InvoiceLines] DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];GO -- Onze duidelijke resultatensetSELECT DISTINCT i.InvoiceIDFROM Sales.InvoiceLines il RIGHT JOIN Sales.Invoices i AAN il.InvoiceID =i.InvoiceID;
Beelden
Een voordeel van het verwijderen van joins is dat het kan werken met views, zelfs als de onderliggende view-query geen join-eliminatie kan gebruiken:
-- Voeg onze FK ALTER TABLE [Sales].[InvoiceLines] toe MET CONTROLE ADD CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] FOREIGN KEY([InvoiceID])REFERENTIES [Sales].[Facturen] ([InvoiceID]);GO -- Create onze mening met behulp van een query die geen gebruik kan maken van join-eliminatieCREATE VIEW Sales.vInvoicesAndInvoiceLinesAS SELECT i.InvoiceID, i.InvoiceDate, il.Quantity, il.TaxRate FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID; GO -- Join eliminatie werkt omdat we geen -- kolommen selecteren uit de onderliggende Sales.Invoices tabel SELECT Quantity, TaxRate FROM Sales.vInvoicesAndInvoiceLines;
Conclusie
Het elimineren van joins is een optimalisatie die SQL Server uitvoert wanneer wordt vastgesteld dat het een nauwkeurige resultatenset kan bieden zonder dat gegevens hoeven te worden gelezen uit alle tabellen die zijn opgegeven in de ingediende query. Deze optimalisatie kan aanzienlijke prestatieverbeteringen opleveren door het aantal pagina's dat SQL Server moet lezen te verminderen, maar dit gaat vaak ten koste van het handhaven van bepaalde databasebeperkingen. We kunnen query's herstructureren om de eenvoudigere uitvoeringsplannen te bereiken die het verwijderen van joins biedt, maar het is een mooi voordeel dat de query-optimizer onze plannen automatisch vereenvoudigt door onnodige joins te verwijderen.
Nogmaals, ik nodig je uit om de volledige videoversie van dit bericht te bekijken.
Over de auteur
Bert is een ontwikkelaar van business intelligence uit Cleveland, Ohio. Hij houdt van het schrijven van snellopende query's en helpt anderen graag om zelfvoorzienende SQL-probleemoplossers te worden. Bert blogt over SQL Server op bertwagner.com en maakt SQL Server YouTube-video's op youtube.com/c/bertwagner.