sql >> Database teknologi >  >> RDS >> Database

Rækkemål, del 3:Anti Joins

Dette indlæg er en del af en række artikler om rækkemål. Du kan finde de andre dele her:

  • Del 1:Opstilling og identifikation af rækkemål
  • Del 2:Semi-joins

Denne del dækker, hvornår og hvorfor optimeringsværktøjet introducerer et rækkemål for en anti-join.

Introduktion

En anti join er også kendt som en anti semi join. Det returnerer hver række fra join-input A, som ingen match for kan findes på input B.

For en anti join:

  • Optimeringsværktøjet kan føj et mål på den indre række til en anvend (korrelerede indlejrede loops join) kun anti join .
  • Et rækkemål er ikke tilføjet for ikke-korrelerede indlejrede loops anti join, hash anti join eller merge anti join.
  • Som altid tilføjes ethvert rækkemål kun hvis det er lavere end estimatet uden et rækkemål anvendt.
  • Redundant TOP på indersiden klausuler og DISTINCT/GROUP BY operationer kan forenkles væk.

Udvidende på det første punkt ovenfor, er hovedforskellen mellem anvendelse af semi join og anvend anti join-rækkemål:

  • En anvend semi-join har altid et rækkemål (så længe det er mindre end skønnet uden målet).
  • En anvend anti joinforbindelse kan indeholde et rækkemål , men kun hvis en logisk anti-join omdannes til en application under omkostningsbaseret optimering .

Jeg beklager, at disse regler ikke er enklere, men jeg har ikke lavet dem. Forhåbentlig vil nogle diskussioner og eksempler gøre det hele mere klart.

Ingen Anti Join Row-mål som standard

Optimeringsværktøjet antager, at folk skriver en semi join (indirekte, f.eks. ved at bruge EXISTS ) med forventning om, at den række, der søges efter, vil blive fundet . Der er angivet et rækkemål for at anvende semi join af optimeringsværktøjet for hurtigt at finde den forventede matchende række.

For anti join (udtrykt f.eks. ved hjælp af NOT EXISTS ) optimeringsværktøjets antagelse er, at en matchende række ikke bliver fundet . Der er ikke angivet et rækkemål mod tilslutning af optimeringsværktøjet, fordi det forventer at skulle tjekke alle rækker for at bekræfte, at der ikke er nogen match.

Hvis der viser sig at være en matchende række, kan det tage længere tid at lokalisere denne række, end hvis et rækkemål var blevet brugt. Ikke desto mindre vil anti-join stadig afslutte sin søgning, så snart det (uventede) match er stødt på.

Anvend Anti Join Row Goal-betingelser

SQL Server giver os ikke mulighed for at skrive en anti-join direkte, så vi er nødt til at bruge løsninger som NOT EXISTS , NOT IN/ANY/SOME , eller EXCEPT . Hver af disse formularer resulterer i en underforespørgselsrepræsentation i det parsede træ ved begyndelsen af ​​forespørgselskompileringen. Denne underforespørgsel rulles altid ud til en ansøgning og omdannes derefter til en logisk anti-join hvor det er muligt (detaljerne er de samme som for semi join diskuteret i del 2). Alt dette sker, før selv en triviel plan overvejes.

For at en anti join kan få et rækkemål skal den enter omkostningsbaseret optimering som en logisk anti join (hvilket betyder, at transformationen fra en ansøgning ovenfor skal have været vellykket). Derefter skal den omkostningsbaserede optimizer vælge at implementere den logiske anti join som en anvend . For at dette kan ske, skal optimeringsværktøjet først vælge at udforske muligheden anvende; så skal den vælge det som den billigste mulighed (for den del af planen).

Et anti join-rækkemål er fastsat af en af ​​de omkostningsbaserede optimeringsregler, der kan transformere en joinforbindelse til en application. En anti join, der træder ind omkostningsbaseret optimering som anvendelse (fordi transformationen til logisk anti join mislykkedes) vil ikke har et rækkemål anvendt.

Den omkostningsbaserede optimering vil kun udforske og vælge muligheden sammenføj for at ansøge, hvis der er en effektiv måde at finde matchende indvendige siderækker (f.eks. ved hjælp af et indeks). Optimeringsværktøjet vil ikke udforske muligheden, hvis undertræet på den indvendige side af sammenføjningen mangler noget nyttigt for anvendelsesprædikatet til at "låse fast på". Dette kan være et indeks, et midlertidigt indeks (via en ivrig indeksspole) eller en anden logisk nøgle. Tilføjelse af rækkemålet hjælper optimeringsværktøjet med at vurdere prisen på muligheden for deltagelse for at ansøge, da der maksimalt skal placeres én række.

Bemærk, at en anvend anti-join kan forekomme i en eksekveringsplan uden et rækkemål. Dette sker, når den indledende transformation fra anvende til join mislykkes, hvilket er relativt almindeligt. Når dette sker, starter anti-sammenføjningen livet i den omkostningsbaserede optimering som en ansøgning, og derfor er der aldrig tilføjet et rækkemål af en af ​​sammenføjning-til-anvend-reglerne.

Selvfølgelig kan et rækkemål også indføres på indersiden af ​​denne ansøgning via en anden mekanisme (ikke forbundet med ansøgningen), for eksempel af en separat Top-operatør.

For at opsummere:

  • En anti-join kan kun opnå et rækkemål under omkostningsbaseret optimering (CBO).
  • Regler, der oversætter en anti-join til en anvende, tilføjer et rækkemål.
  • Anti joinforbindelsen skal indtaste CBO som en join, ikke en ansøgning.
  • For at indtaste CBO som en joinforbindelse, skal tidligere faser være i stand til at omskrive underforespørgslen som en joinforbindelse (via en ansøgningsfase).
  • CBO udforsker kun joinforbindelsen for at anvende transformation i lovende tilfælde.

Eksempel

Det er lidt mere tricky at demonstrere alt dette for at anvende anti join, end det var tilfældet for at anvende semi join. Årsagerne til dette vil blive dækket i del 4.

I mellemtiden er her et AdventureWorks-eksempel, der viser, hvordan en anvende anti-join med rækkemål opstår, ved at bruge de samme udokumenterede sporingsflag som for semi join. Sporingsflag 8608 er tilføjet for at vise den indledende memostruktur ved starten af ​​omkostningsbaseret optimering.

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);

Den eksisterende underforespørgsel omdannes først til en anvende: