sql >> Databáze >  >> RDS >> Sqlserver

NENÍ IN vs NEEXISTUJE

Vždy mám výchozí hodnotu NOT EXISTS .

Prováděcí plány mohou být v tuto chvíli stejné, ale pokud bude některý sloupec v budoucnu změněn tak, aby umožňoval NULL je NOT IN verze bude muset udělat více práce (i když žádná NULL s jsou skutečně přítomné v datech) a sémantika NOT IN pokud NULL s jsou přítomné pravděpodobně stejně nebudou ty, které chcete.

Když ani Products.ProductID nebo [Order Details].ProductID povolit NULL je NOT IN bude zpracováno stejně jako následující dotaz.

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId) 

Přesný plán se může lišit, ale pro můj příklad dat dostanu následující.

Zdá se, že přiměřeně běžnou mylnou představou je, že korelované dílčí dotazy jsou vždy „špatné“ ve srovnání se spojeními. Určitě mohou být, když vynutí plán vnořených smyček (dílčí dotaz hodnocený řádek po řádku), ale tento plán obsahuje logický operátor proti semi spojení. Anti semi spojení nejsou omezena na vnořené smyčky, ale mohou také používat hash nebo merge (jako v tomto příkladu).

/*Not valid syntax but better reflects the plan*/ 
SELECT p.ProductID,
       p.ProductName
FROM   Products p
       LEFT ANTI SEMI JOIN [Order Details] od
         ON p.ProductId = od.ProductId 

Pokud [Order Details].ProductID je NULL -able se pak dotaz stane

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL) 

Důvodem je správná sémantika if [Order Details] obsahuje jakékoli NULL ProductId s je nevracet žádné výsledky. Podívejte se na extra anti semi spojení a cívku počtu řádků, abyste si ověřili, že je to přidáno do plánu.

Pokud Products.ProductID se také změní na NULL -able se pak dotaz stane

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL)
       AND NOT EXISTS (SELECT *
                       FROM   (SELECT TOP 1 *
                               FROM   [Order Details]) S
                       WHERE  p.ProductID IS NULL) 

Důvodem je, že NULL Products.ProductId by neměly být vráceny ve výsledcích s výjimkou pokud NOT IN dílčí dotaz neměl vrátit vůbec žádné výsledky (tj. [Order Details] tabulka je prázdná). V tom případě by mělo. V plánu pro moje ukázková data je to implementováno přidáním dalšího anti semi spojení, jak je uvedeno níže.

Účinek tohoto je ukázán v blogovém příspěvku, na který již odkazoval Buckley. V tomto příkladu se počet logických čtení zvýšil z přibližně 400 na 500 000.

Navíc skutečnost, že jeden NULL může snížit počet řádků na nulu, což velmi ztěžuje odhad mohutnosti. Pokud SQL Server předpokládá, že k tomu dojde, ale ve skutečnosti nebyly žádné NULL řádků v datech zbytek prováděcího plánu může být katastrofálně horší, pokud je to jen část většího dotazu, s nevhodnými vnořenými smyčkami, které způsobí opakované spouštění například drahého podstromu.

Toto není jediný možný plán provedení pro NOT IN na NULL -schopný sloup však. Tento článek ukazuje další pro dotaz na AdventureWorks2008 databáze.

Pro NOT IN na NOT NULL nebo NOT EXISTS proti sloupci s možnou hodnotou null nebo bez možnosti null poskytuje následující plán.

Když se sloupec změní na NULL -povolit NOT IN plán nyní vypadá takto

Do plánu přidává další operátor vnitřního spojení. Toto zařízení je vysvětleno zde. Je to vše pro převod předchozího hledání jediného korelovaného indexu na Sales.SalesOrderDetail.ProductID = <correlated_product_id> na dvě hledání na vnější řadu. Další je na WHERE Sales.SalesOrderDetail.ProductID IS NULL .

Vzhledem k tomu, že se jedná o anti semi spojení, pokud tento vrátí nějaké řádky, druhé hledání nenastane. Pokud však Sales.SalesOrderDetail neobsahuje žádné NULL ProductId s zdvojnásobí počet požadovaných operací hledání.



  1. Příklad autonomní transakce Oracle

  2. Jak seřadit podle počtu v SQL?

  3. Zjistěte, zda je tabulka rozdělena na SQL Server (T-SQL)

  4. Chyba mysql node.js:ECONNREFUSED