sql >> Databáze >  >> RDS >> Database

Row Goals, část 3:Anti Joins

Tento příspěvek je součástí série článků o cílech řádků. Ostatní díly najdete zde:

  • Část 1:Stanovení a identifikace cílů řady
  • Část 2:Semináře

Tato část popisuje, kdy a proč optimalizátor zavádí cíl řádku pro anti spojení.

Úvod

Anti spojení je také známé jako anti semi spojení. Vrátí každý řádek ze vstupu spojení A, pro který žádná shoda naleznete na vstupu B.

Pro anti join:

  • Optimalizátor může přidat k použití cíl vnitřního řádku (korelované spojení vnořených smyček) pouze proti spojení .
  • Cíl řádku není přidán pro nekorelované vnořené smyčky anti join, hash anti join nebo merge anti join.
  • Jako vždy je cíl každého řádku pouze přidán pokud je nižší než odhad bez použití cíle řádku.
  • Nadbytečná vnitřní strana TOP klauzule a DISTINCT/GROUP BY operace lze zjednodušit.

Když to rozšíříme o první odrážku výše, hlavní rozdíl mezi cíli použít semi-spojení a použít cíle proti spojení je:

  • Použít semi spojení vždy obsahuje cíl řádku (pokud je menší než odhad bez cíle).
  • Použití proti připojení může obsahovat cíl řádku , ale pouze v případě, že se logické anti spojení při optimalizaci založené na nákladech přemění na aplikaci .

Omlouvám se, že tato pravidla nejsou jednodušší, ale nevytvořil jsem je. Doufejme, že nějaká diskuse a příklady vše objasní.

Ve výchozím nastavení žádný cíl Anti Join Row

Optimalizátor předpokládá, že lidé píší semi join (nepřímo, např. pomocí EXISTS ) s očekáváním, že hledaný řádek bude nalezen . Je nastaven cíl řádku optimalizátorem, aby pomohl rychle najít očekávaný odpovídající řádek.

Pro anti připojení (vyjádřeno např. pomocí NOT EXISTS ) optimalizátor předpokládá, že odpovídající řádek nebude nalezen . cíl řádku není nastaven optimalizátorem, protože očekává, že bude muset zkontrolovat všechny řádky, aby potvrdil, že neexistuje žádná shoda.

Pokud se ukáže, že existuje odpovídající řádek, může použití proti spojení trvat déle, než vyhledá tento řádek, než kdyby byl použit cíl řádku. Nicméně anti join stále ukončí své hledání, jakmile narazí na (neočekávanou) shodu.

Použít podmínky cíle Anti Join Row

SQL Server nám neposkytuje způsob, jak přímo napsat anti spojení, takže musíme použít zástupná řešení jako NOT EXISTS , NOT IN/ANY/SOME nebo EXCEPT . Každý z těchto formulářů má za následek reprezentaci poddotazu v analyzovaném stromu na začátku kompilace dotazu. Tento dílčí dotaz se vždy rozvine do aplikace a poté se transformuje na logické anti spojení pokud je to možné (podrobnosti jsou stejné jako u semi spojení probíraného v části 2). To vše se děje ještě předtím, než se uvažuje o triviálním plánu.

Aby anti spojení získalo cíl řádku, musí vstoupit optimalizace založená na nákladech jako logický anti join (to znamená, že transformace z výše uvedené žádosti musí být úspěšná). Poté se musí optimalizátor založený na nákladech rozhodnout implementovat logické anti spojení jako aplikaci . Aby k tomu došlo, musí optimalizátor nejprve zvolit prozkoumat možnost použít; pak musí vybrat to jako nejlevnější možnost (pro tuto část plánu).

Cíl proti řádkovému spojení je nastaven libovolným optimalizačním pravidlem založeným na nákladech, které může transformovat spojení na žádost. Anti připojení, které vstoupí optimalizace založená na nákladech jako použití (protože transformace na logické anti spojení se nezdařila) nebude použít cíl řádku.

Optimalizátor založený na nákladech prozkoumá a vybere možnost spojení s aplikací pouze v případě, že existuje účinný způsob, jak najít všechny odpovídající řádky na vnitřní straně (např. pomocí indexu). Optimalizátor tuto možnost neprozkoumá, pokud podstrom vnitřní strany spojení postrádá cokoli užitečného pro predikát použít, aby se „zachytil na“. Může to být index, dočasný index (prostřednictvím eager index spool) nebo jiný logický klíč. Přidání cíle řádku pomůže optimalizátoru posoudit náklady na možnost spojení s aplikací vzhledem k tomu, že je třeba najít maximálně jeden řádek.

Všimněte si, že použití anti spojení se může objevit v plánu provádění bez cíle řádku. K tomu dochází, když selže počáteční transformace z aplikace na spojení, což je poměrně běžné. Když k tomu dojde, anti spojení začne fungovat v optimalizátoru založeném na nákladech jako žádost, takže nikdy nebude přidán cíl řádku jedním z pravidel spojení s aplikací.

Samozřejmě, že cíl řádku může být také zaveden na vnitřní straně této aplikace prostřednictvím jiného mechanismu (není spojen s aplikací), například samostatným horním operátorem.

Abych to shrnul:

  • Anti join může získat cíl řádku pouze během optimalizace založené na nákladech (CBO).
  • Pravidla, která převádějí anti spojení na aplikaci, přidávají cíl řádku.
  • Anti join musí zadat CBO jako spojení, nikoli žádost.
  • Chcete-li zadat CBO jako spojení, musí být dřívější fáze schopny přepsat poddotaz jako spojení (prostřednictvím fáze použití).
  • CBO zkoumá spojení k aplikaci transformace pouze ve slibných případech.

Příklad

Je trochu složitější demonstrovat toto vše pro použít anti spojení, než tomu bylo v případě použít semi spojení. Důvody pro to budou popsány v části 4.

Mezitím je zde příklad AdventureWorks, který ukazuje, jak vzniká použití proti spojení s cílem řádku pomocí stejných nedokumentovaných příznaků trasování jako pro semi spojení. Je přidán příznak trasování 8608, který ukazuje počáteční strukturu poznámky na začátku optimalizace založené na nákladech.

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

Poddotaz exists se nejprve transformuje na aplikaci Apply: