Gæsteforfatter:Bert Wagner (@bertwagner)
Eliminering af deltagelse er en af de mange teknikker, som SQL Server-forespørgselsoptimeringsværktøjet bruger til at skabe effektive forespørgselsplaner. Konkret opstår eliminering af joinforbindelser, når SQL Server kan etablere lighed ved at bruge forespørgselslogik eller betroede databasebegrænsninger for at eliminere unødvendige joinforbindelser. Se en fuld videoversion af dette indlæg på min YouTube-kanal.
Deltag Elimination In Action
Den enkleste måde at forklare eliminering af deltagelse på er gennem en række demoer. Til disse eksempler vil jeg bruge WideWorldImporters-demodatabasen.
For at starte tingene, vil vi se på, hvordan join-eliminering fungerer, når en fremmednøgle er til stede:
VÆLG il.* FRA Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
I dette eksempel returnerer vi kun data fra Sales.InvoiceLines, hvor der findes et matchende InvoiceID i Sales.Invoices. Selvom du måske forventer, at eksekveringsplanen viser en join-operatør på tabellerne Sales.InvoiceLines og Sales.Invoices, gider SQL Server overhovedet ikke se på Sales.Invoices:
SQL Server undgår at slutte sig til tabellen Sales.Invoices, fordi den har tillid til den referenceintegritet, der opretholdes af den fremmednøglebegrænsning, der er defineret på InvoiceID mellem Sales.InvoiceLines og Sales.Invoices; hvis der findes en række i Sales.InvoiceLines, skal en række med den matchende værdi for InvoiceID findes i Sales.Invoices. Og da vi kun returnerer data fra Sales.InvoiceLines-tabellen, behøver SQL Server slet ikke at læse nogen sider fra Sales.Invoices.
Vi kan bekræfte, at SQL Server bruger den fremmede nøgle-begrænsning til at eliminere joinforbindelsen ved at droppe begrænsningen og køre vores forespørgsel igen:
ÆNDRINGSTABEL [Salg].[InvoiceLines] DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];
Uden information om forholdet mellem vores to tabeller er SQL Server tvunget til at udføre en joinforbindelse og scanne et indeks på vores Sales.Invoices-tabel for at finde matchende InvoiceID'er.
Fra et I/O-synspunkt skal SQL Server læse yderligere 124 sider fra et indeks på Sales.Invoices-tabellen, og det er kun fordi den er i stand til at bruge et smalt (enkelt kolonne) indeks skabt af en anden fremmednøgle-begrænsning. Dette scenarie kan blive meget værre på større borde eller borde, der ikke er indekseret korrekt.
Begrænsninger
Mens det forrige eksempel viser det grundlæggende i, hvordan eliminering af medlemskab fungerer, skal vi være opmærksomme på nogle få forbehold.
Lad os først tilføje vores fremmednøglebegrænsning:
ÆNDRINGSTABEL [Salg].[Fakturalinjer] MED NOCHECK ADD CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] UDENLANDSKE NØGLE([InvoiceID])REFERENCER [Salg].[Fakturaer] ([FakturaID]);
Hvis vi kører vores eksempelforespørgsel igen, vil vi bemærke, at vi ikke får en plan, der udviser eliminering af medlemskab; i stedet får vi en plan, der scanner begge vores sammenføjede tabeller.
Årsagen til dette sker, er fordi, da vi gentilføjede vores fremmednøgle-begrænsning, ved SQL Server ikke, om nogen data er blevet ændret i mellemtiden. Eventuelle nye eller ændrede data overholder muligvis ikke denne begrænsning, så SQL Server kan ikke stole på gyldigheden af vores data:
SELECT f.name AS udenlandsk_nøgle_navn ,OBJECT_NAME(f.parent_object_id) AS tabelnavn ,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.Invoice');For at genetablere SQL Servers tillid til denne begrænsning, skal vi kontrollere dens gyldighed:
ÆNDRINGSTABEL [Salg].[InvoiceLines] MED KONTROLBEGRÆNSNING [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];På store tabeller kan denne handling tage noget tid, for ikke at nævne overheaden af SQL Server, der validerer disse data under hver indsættelse/opdatering/slet ændring fremover.
En anden begrænsning er, at SQL Server ikke kan eliminere sammenføjede tabeller, når forespørgslen skal returnere data fra disse potentielle elimineringskandidater:
SELECT il.*, i.InvoiceDateFROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Eliminering af medlemskab forekommer ikke i forespørgslen ovenfor, fordi vi anmoder om, at data fra Sales.Invoices returneres, hvilket tvinger SQL Server til at læse data fra den tabel.
Endelig er det vigtigt at bemærke, at join-eliminering ikke vil forekomme, når fremmednøglen har flere kolonner, eller hvis tabellerne er i tempdb. Sidstnævnte er en af flere grunde til, at du ikke bør forsøge at løse optimeringsproblemer ved at kopiere dine tabeller til tempdb.
Yderligere scenarier
Flere tabeller
Join-eliminering er ikke kun begrænset til to-bords indre joinforbindelser og tabeller med fremmednøgle-begrænsninger.
For eksempel kan vi oprette en ekstra tabel, der refererer til vores Sales.Invoices.InvoiceID-kolonne:
OPRET TABEL Sales.InvoiceClickTracking (InvoiceClickTrackingID bigint IDENTITY PRIMÆR NØGLE, InvoiceID int -- andre felter vil gå her); GÅ ÆNDRINGSTABEL [Salg].[InvoiceClickTracking] MED KONTROL TILFØJ BEGRÆNSNING [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices] UDENLANDSKE NØGLE([InvoiceID]) REFERENCER [Salg].[Fakturaer] ([FakturaID]);Sammenføjning af denne tabel i vores originale eksempelforespørgsel vil også give SQL Server mulighed for at fjerne vores Sales.Invoices-tabel:
VÆLG il.InvoiceID, ict.InvoiceID FRA 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 fjerne Sales.Invoices-tabellen på grund af den transitive tilknytning mellem disse tabellers relationer.
Unikke begrænsninger
I stedet for en fremmednøgle-begrænsning vil SQL Server også udføre join-eliminering, hvis den kan stole på datarelationen med en unik begrænsning:
ÆNDRINGSTABEL [Salg].[InvoiceClickTracking] DROP CONSTRAINT [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices]; GÅ ÆNDRINGSTABEL Salg.InvoiceClickTracking TILFØJ BEGRÆNSNING UQ_InvoiceID UNIK (FakturaID); GÅ VÆLG i.InvoiceID FRA Sales.InvoiceClickTracking ict HØJRE JOIN Sales.Invoices i ON ict.InvoiceID =i.InvoiceID;
Ydre sammenføjninger
Så længe SQL Server kan udlede relationsbegrænsninger, kan andre typer joinforbindelser også opleve tabeleliminering. For eksempel:
VÆLG il.InvoiceIDFROM Sales.InvoiceLines il LEFT JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceIDDa vi stadig har vores fremmednøgle-begrænsning, der håndhæver, at hvert InvoiceID i Sales.InvoiceLines skal have et tilsvarende InvoiceID i Sales.Invoices, har SQL Server ingen problemer med at returnere alt fra Sales.InvoiceLines uden at skulle tilsluttes Sales.Invoices:
Ingen begrænsning påkrævet
Hvis SQL Server kan garantere, at den ikke har brug for data fra en bestemt tabel, kan den potentielt eliminere en joinforbindelse.
Der forekommer ingen join-eliminering i denne forespørgsel, fordi SQL Server ikke kan identificere, om forholdet mellem Sales.Invoices og Sales.InvoiceLines er 1-til-1, 1-til-0 eller 1-til-mange. Den er tvunget til at læse Sales.InvoiceLines for at afgøre, om der findes nogle matchende rækker:
VÆLG i.InvoiceIDFROM Sales.InvoiceLines il RIGHT JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Men hvis vi angiver, at vi ønsker et DISTINKT sæt af i.InvoiceID'er, returnerer hver unik værdi fra Sales.Invoices fra SQL Server, uanset hvilket forhold disse rækker har til Sales.InvoiceLines.
-- Bare for at bevise, at der ikke er nogen fremmednøgle i spil her ALTER TABLE [Salg].[Fakturalinjer] DROP BEGRÆNSNING [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];GO -- Vores særskilte resultatsætSELECT DISTINCT i.InvoiceIDFROM Sales.InvoiceLines Sales.InvoiceLines R.InvoiceLines i ON il.InvoiceID =i.InvoiceID;
Visninger
En fordel ved join-eliminering er, at den kan fungere med visninger, selvom den underliggende visningsforespørgsel ikke er i stand til at bruge join-eliminering:
-- Tilføj tilbage vores FK ALTER TABLE [Salg].[Fakturalinjer] MED KONTROL TILFØJ BEGRÆNSNING [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] UDENLANDSKE NØGLE([InvoiceID])REFERENCER [Salg].[Fakturaer] ([GO -- Opret ID]); vores visning ved hjælp af en forespørgsel, der ikke kan bruge join eliminationCREATE VIEW Sales.vInvoicesAndInvoiceLinesAS SELECT i.InvoiceID, i.InvoiceDate, il.Quantity, il.TaxRate FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON =i.InvoiceID; GO -- Eliminering af medlemskab virker, fordi vi ikke vælger nogen -- kolonner fra den underliggende Sales.Invoices-tabel VÆLG Mængde, TaxRate FRA Sales.vInvoicesAndInvoiceLines;
Konklusion
Join-eliminering er en optimering, som SQL Server udfører, når den bestemmer, at den kan give et nøjagtigt resultatsæt uden at skulle læse data fra alle tabeller, der er angivet i den indsendte forespørgsel. Denne optimering kan give betydelige ydeevneforbedringer ved at reducere antallet af sider, SQL Server skal læse, men det sker ofte på bekostning af behovet for at opretholde visse databasebegrænsninger. Vi kan refaktorisere forespørgsler for at opnå de enklere udførelsesplaner, som join-eliminering giver, men det er en god fordel at have forespørgselsoptimeringsværktøjet til automatisk at forenkle vores planer ved at fjerne unødvendige joinforbindelser.
Igen inviterer jeg dig til at se den fulde videoversion af dette indlæg.
Om forfatteren
Bert er en business intelligence-udvikler fra Cleveland, Ohio. Han elsker at skrive hurtige forespørgsler og nyder at hjælpe andre med at lære at være selvforsynende med SQL-problemløsere. Bert blogger om SQL Server på bertwagner.com og laver SQL Server YouTube-videoer på youtube.com/c/bertwagner.