sql >> Databasteknik >  >> RDS >> Sqlserver

INTE IN vs INTE FINNS

Jag har alltid som standard FINNS INTE .

Utförandeplanerna kan vara desamma för tillfället men om någon av kolumnerna ändras i framtiden för att tillåta NULL är NOT IN version kommer att behöva göra mer arbete (även om ingen NULL s faktiskt finns i datan) och semantiken för NOT IN om NULL s är närvarande är sannolikt inte de du vill ha ändå.

När varken Products.ProductID eller [Beställningsinformation].ProduktID tillåt NULL är NOT IN kommer att behandlas identiskt med följande fråga.

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

Den exakta planen kan variera men för mina exempeldata får jag följande.

En ganska vanlig missuppfattning verkar vara att korrelerade underfrågor alltid är "dåliga" jämfört med joins. De kan säkert vara det när de tvingar fram en kapslad loopplan (underfrågan utvärderas rad för rad), men den här planen innehåller en logisk anti-semi-join-operator. Anti-semi-joins är inte begränsade till kapslade loopar utan kan också använda hash- eller merge-kopplingar (som i det här exemplet).

/*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 

Om [Beställningsinformation].ProduktID är NULL -kan blir frågan

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) 

Anledningen till detta är att korrekt semantik om [Beställningsdetaljer] innehåller någon NULL ProductId s är att inte returnera några resultat. Se den extra anti semi join och radräkningsspolen för att verifiera detta som läggs till i planen.

Om Products.ProductID ändras också till att bli NULL -kan blir frågan

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) 

Anledningen till det är att en NULL Products.ProductId ska inte returneras i resultaten förutom om NOT IN underfrågan skulle inte returnera några resultat alls (d.v.s. [Beställningsinformation] tabellen är tom). I så fall borde det. I planen för mina exempeldata implementeras detta genom att lägga till ytterligare en anti semi-join enligt nedan.

Effekten av detta visas i blogginlägget som redan länkats av Buckley. I exemplet där ökar antalet logiska läsningar från cirka 400 till 500 000.

Dessutom det faktum att en enda NULL kan minska radantalet till noll gör kardinalitetsuppskattningen mycket svår. Om SQL Server antar att detta kommer att hända men i själva verket fanns det ingen NULL rader i data resten av exekveringsplanen kan vara katastrofalt värre, om detta bara är en del av en större fråga, med olämpliga kapslade loopar som orsakar upprepad exekvering av ett dyrt underträd till exempel.

Detta är inte den enda möjliga exekveringsplanen för en NOT IN på en NULL -kan kolumn dock. Den här artikeln visar en annan för en fråga mot AdventureWorks2008 databas.

För NOT IN på en NOT NULL kolumnen eller FINNS INTE mot antingen en nullbar eller icke nullbar kolumn ger den följande plan.

När kolumnen ändras till NULL -kan NOT IN planen ser nu ut

Det lägger till en extra inre sammanfogningsoperatör till planen. Denna apparat förklaras här. Allt finns där för att konvertera den tidigare enstaka korrelerade indexsökningen på Sales.SalesOrderDetail.ProductID = till två sökningar per yttre rad. Den ytterligare finns på WHERE Sales.SalesOrderDetail.ProductID IS NULL .

Eftersom detta är under en anti semi-join om den returnerar några rader kommer den andra sökningen inte att ske. Men om Sales.SalesOrderDetail innehåller ingen NULL Produkt-ID s det kommer att fördubbla antalet sökoperationer som krävs.



  1. Effektiv övervakning av MySQL-replikering med SCUMM Dashboards:Del 2

  2. Postgres tid med tidszonslikhet

  3. MySQL-utlösare vid Infoga/Uppdatera händelser

  4. Infoga flera rader med en MySQL-fråga