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

IKKE IN vs IKKE FINDER

Som standard er jeg altid EKSISTERER IKKE .

Udførelsesplanerne kan være de samme i øjeblikket, men hvis en af ​​kolonnerne ændres i fremtiden for at tillade NULL er NOT IN version skal gøre mere arbejde (selvom ingen NULL s faktisk er til stede i dataene) og semantikken for NOT IN hvis NULL s er tilstedeværende er usandsynligt at være dem, du ønsker alligevel.

Når hverken Products.ProductID eller [Ordredetaljer].ProductID tillad NULL er NOT IN vil blive behandlet identisk med følgende forespørgsel.

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

Den nøjagtige plan kan variere, men for mine eksempeldata får jeg følgende.

En rimelig almindelig misforståelse synes at være, at korrelerede underforespørgsler altid er "dårlige" sammenlignet med joinforbindelser. Det kan de bestemt være, når de tvinger en indlejret sløjfeplan (underforespørgsel evalueret række for række), men denne plan indeholder en anti-semi join logisk operator. Anti semi joins er ikke begrænset til indlejrede loops, men kan også bruge hash eller merge (som i dette eksempel) joins.

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

Hvis [Ordredetaljer].ProductID er NULL -able forespørgslen bliver derefter

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) 

Grunden til dette er, at den korrekte semantik, hvis [Order Details] indeholder enhver NULL ProductId s er at returnere ingen resultater. Se den ekstra anti-semi join- og rækketællerspole for at bekræfte dette, som er føjet til planen.

Hvis Products.ProductID er også ændret til at blive NULL -able forespørgslen bliver derefter

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) 

Grunden til det er fordi en NULL Products.ProductId skal ikke returneres i resultaterne undtagen hvis NOT IN underforespørgsel skulle ikke returnere nogen resultater overhovedet (dvs. [Order Details] tabellen er tom). I så fald burde det. I planen for mine prøvedata er dette implementeret ved at tilføje en anden anti semi join som nedenfor.

Effekten af ​​dette er vist i blogindlægget, der allerede er linket til af Buckley. I eksemplet stiger antallet af logiske læsninger fra omkring 400 til 500.000.

Derudover det faktum, at en enkelt NULL kan reducere rækkeantallet til nul gør det meget vanskeligt at estimere kardinalitet. Hvis SQL Server antager, at dette vil ske, men der faktisk ikke var nogen NULL rækker i dataene resten af ​​udførelsesplanen kan være katastrofalt værre, hvis dette blot er en del af en større forespørgsel, med uhensigtsmæssige indlejrede løkker, der for eksempel forårsager gentagen eksekvering af et dyrt undertræ.

Dette er ikke den eneste mulige udførelsesplan for en NOT IN på en NULL -stand kolonne dog. Denne artikel viser en anden for en forespørgsel mod AdventureWorks2008 database.

For NOT IN på en NOT NULL kolonne eller EKSISTERER IKKE mod enten en nullbar eller ikke-nullbar kolonne giver den følgende plan.

Når kolonnen ændres til NULL -i stand til NOT IN planen ser nu ud

Det tilføjer en ekstra indre joinoperatør til planen. Dette apparat er forklaret her. Det hele er der for at konvertere den tidligere enkelte korrelerede indekssøgning på Sales.SalesOrderDetail.ProductID = til to søgninger pr. yderste række. Den yderligere er på WHERE Sales.SalesOrderDetail.ProductID IS NULL .

Da dette er under en anti semi join, hvis den ene returnerer nogen rækker, vil den anden søgning ikke forekomme. Men hvis Sales.SalesOrderDetail indeholder ingen NULL Produkt-id s det vil fordoble antallet af krævede søgeoperationer.



  1. Sådan indstilles forbindelsestimeout i SQLAlchemy

  2. Sådan fungerer ASCII() i MariaDB

  3. SSMS-resultater til gitter - CRLF er ikke bevaret i copy/paste - nogen bedre teknikker?

  4. Java-type for dato/klokkeslæt, når du bruger Oracle Date med Hibernate