sql >> Databasteknik >  >> RDS >> Database

Row Goals, del 3:Anti Joins

Det här inlägget är en del av en serie artiklar om radmål. Du hittar de andra delarna här:

  • Del 1:Ställa in och identifiera radmål
  • Del 2:Semi-joins

Den här delen tar upp när och varför optimeraren introducerar ett radmål för en anti-join.

Introduktion

En anti-join är också känd som en anti semi-join. Den returnerar varje rad från joiningång A som ingen matchning för kan hittas på ingång B.

För en anti-anslutning:

  • Optimeraren kan lägg till ett mål på innerraden i en applicera (korrelerade kapslade slingor går samman) enbart anti-join .
  • Ett radmål är inte lagt till för icke-korrelerade kapslade loopar anti join, hash anti join eller merge anti join.
  • Som alltid läggs alla radmål bara till om det är lägre än uppskattningen utan ett radmål tillämpat.
  • Redundant innersida TOP satser och DISTINCT/GROUP BY operationer kan förenklas bort.

För att utöka den första punkten ovan, är huvudskillnaden mellan applicera semi join och applicera anti join rad-mål:

  • En ansök halvanslutning har alltid ett radmål (så länge det är mindre än uppskattningen utan målet).
  • En apply anti join kan ha ett radmål , men bara om en logisk antikoppling omvandlas till en applicering under kostnadsbaserad optimering .

Jag ber om ursäkt för att dessa regler inte är enklare, men jag har inte gjort dem. Förhoppningsvis kommer några diskussioner och exempel att göra det hela tydligare.

Inga Anti Join Row-mål som standard

Optimeraren förutsätter att människor skriver en semi join (indirekt t.ex. genom att använda EXISTS ) med förväntningen att raden som söks efter kommer att hittas . Ett radmål för applicering av halvanslutning är satt av optimeraren för att snabbt hitta den förväntade matchande raden.

För anti join (uttryckt t.ex. med NOT EXISTS ) optimerarens antagande är att en matchande rad inte kommer att hittas . Ett radmål för applicering mot anslutning är inte satt av optimeraren, eftersom den förväntar sig att behöva kontrollera alla rader för att bekräfta att det inte finns någon matchning.

Om det visar sig att det finns en matchande rad, kan det ta längre tid för appliceringsskyddet att hitta denna rad än om ett radmål hade använts. Ändå kommer anti-join fortfarande att avsluta sin sökning så snart den (oväntade) matchningen påträffas.

Tillämpa villkor för Anti Join Row-mål

SQL Server ger oss inte ett sätt att skriva en anti-join direkt, så vi måste använda lösningar som NOT EXISTS , NOT IN/ANY/SOME , eller EXCEPT . Var och en av dessa formulär resulterar i en underfrågarepresentation i det analyserade trädet i början av frågekompileringen. Den här underfrågan rullas alltid ut till en app och omvandlas sedan till en logisk anti-join när det är möjligt (detaljerna är desamma som för semi join diskuteras i del 2). Allt detta händer innan ens en trivial plan övervägs.

För att en anti-join ska få ett radmål måste den inträda kostnadsbaserad optimering som en logisk motkoppling (vilket betyder att omvandlingen från en app ovan måste ha lyckats). Sedan måste den kostnadsbaserade optimeraren välja att implementera den logiska antianslutningen som en apply . För att detta ska hända måste optimeraren först välja att utforska alternativet tillämpa; då måste den välja det som det billigaste alternativet (för den delen av planen).

Ett anti-anslutningsrad-mål sätts av någon av de kostnadsbaserade optimeringsreglerna som kan omvandla en sammanfogning till en applicering. En anti-join som träder in kostnadsbaserad optimering som tillämpning (eftersom omvandlingen till logisk anti-join misslyckades) kommer inte har ett radmål tillämpat.

Den kostnadsbaserade optimeraren kommer bara att utforska och välja alternativet gå med för att ansöka om det finns ett effektivt sätt att hitta matchande inre sidorader (t.ex. med hjälp av ett index). Optimeraren kommer inte att undersöka alternativet om underträdet på den inre delen av sammanfogningen saknar något användbart för appliceringspredikatet att "låsa fast vid". Detta kan vara ett index, ett tillfälligt index (via en ivrig indexspole) eller annan logisk nyckel. Att lägga till radmålet hjälper optimeraren att bedöma kostnaden för alternativet gå med för att ansöka med tanke på att högst en rad behöver placeras.

Observera att en applicera anti-join kan förekomma i en exekveringsplan utan ett radmål. Detta inträffar när den initiala omvandlingen från applicera till join misslyckas, vilket är relativt vanligt. När detta händer startar anti-anslutningen livet i den kostnadsbaserade optimeraren som en applicering, och därför har aldrig ett radmål lagts till av någon av join-to-apply-reglerna.

Naturligtvis kan ett radmål också införas på insidan av denna applicering via en annan mekanism (ej associerad med appliceringen), till exempel av en separat Top-operatör.

För att sammanfatta:

  • En anti-join kan bara uppnå ett radmål under kostnadsbaserad optimering (CBO).
  • Regler som översätter en anti-join till en applicering lägger till ett radmål.
  • Anti join måste ange CBO som en join, inte en application.
  • För att ange CBO som en koppling måste tidigare faser kunna skriva om underfrågan som en koppling (via ett appliceringssteg).
  • CBO utforskar bara kopplingen för att tillämpa transformation i lovande fall.

Exempel

Det är lite knepigare att visa allt detta för applicera anti join än vad som var fallet för applicera semi join. Orsakerna till detta kommer att behandlas i del 4.

Samtidigt, här är ett AdventureWorks-exempel som visar hur en applicera anti-join med radmål uppstår, med samma odokumenterade spårningsflaggor som för semi join. Spårningsflagga 8608 läggs till för att visa den initiala memostrukturen i början av kostnadsbaserad 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 existerande underfrågan omvandlas först till en applicera: