sql >> Database >  >> RDS >> Sqlserver

NIET IN vs NIET BESTAAT

Ik gebruik altijd NOT EXISTS .

De uitvoeringsplannen kunnen op dit moment hetzelfde zijn, maar als een van beide kolommen in de toekomst wordt gewijzigd om NULL toe te staan s de NOT IN versie zal meer werk moeten doen (zelfs als er geen NULL is s daadwerkelijk aanwezig zijn in de data) en de semantiek van NOT IN if NULL s zijn Het is toch onwaarschijnlijk dat het cadeau is wat je wilt.

Wanneer geen van beide Products.ProductID of [Order Details].ProductID sta NULL toe s de NOT IN wordt op dezelfde manier behandeld als de volgende vraag.

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

Het exacte plan kan variëren, maar voor mijn voorbeeldgegevens krijg ik het volgende.

Een redelijk algemene misvatting lijkt te zijn dat gecorreleerde subquery's altijd "slecht" zijn in vergelijking met joins. Dat kan zeker wanneer ze een geneste lusplan forceren (subquery wordt rij voor rij geëvalueerd), maar dit plan bevat een logische operator tegen semi-join. Anti-semi-joins zijn niet beperkt tot geneste lussen, maar kunnen ook hash- of merge-joins (zoals in dit voorbeeld) gebruiken.

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

If [Order Details].ProductID is NULL -able de query wordt dan

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) 

De reden hiervoor is dat de juiste semantiek als [Order Details] bevat een NULL ProductId s is om geen resultaten te retourneren. Bekijk de extra anti-semi-join- en rijtellingspoel om te controleren of deze aan het plan is toegevoegd.

Als Products.ProductID is ook veranderd in NULL -able de query wordt dan

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) 

De reden daarvoor is dat een NULL Products.ProductId mag niet worden geretourneerd in de resultaten behalve als de NOT IN subquery zou helemaal geen resultaten opleveren (d.w.z. de [Order Details] tafel is leeg). In dat geval zou het moeten. In het plan voor mijn voorbeeldgegevens wordt dit geïmplementeerd door nog een anti-semi-join toe te voegen, zoals hieronder.

Het effect hiervan is te zien in de blogpost die al door Buckley is gelinkt. In het voorbeeld neemt het aantal logische reads toe van ongeveer 400 tot 500.000.

Bovendien het feit dat een enkele NULL kan het aantal rijen tot nul terugbrengen, maakt het schatten van de kardinaliteit erg moeilijk. Als SQL Server ervan uitgaat dat dit zal gebeuren, maar in feite waren er geen NULL rijen in de gegevens kan de rest van het uitvoeringsplan catastrofaal slechter zijn, als dit slechts een onderdeel is van een grotere query, met ongepaste geneste lussen die bijvoorbeeld de herhaalde uitvoering van een dure subboom veroorzaken.

Dit is niet het enige mogelijke uitvoeringsplan voor een NOT IN op een NULL - staat kolom echter. Dit artikel toont een andere voor een zoekopdracht tegen de AdventureWorks2008 database.

Voor de NOT IN op een NOT NULL kolom of de NOT EXISTS tegen een nullable of niet-nullable kolom geeft het het volgende plan.

Wanneer de kolom verandert in NULL -in staat de NOT IN plan ziet er nu uit als

Het voegt een extra inner join-operator toe aan het plan. Dit apparaat wordt hier uitgelegd. Het is er allemaal om de vorige enkele gecorreleerde indexzoekopdracht te converteren op Sales.SalesOrderDetail.ProductID = <correlated_product_id> tot twee zoekopdrachten per buitenste rij. De extra is op WHERE Sales.SalesOrderDetail.ProductID IS NULL .

Omdat dit onder een anti-semi-join valt, zal de tweede zoekactie niet plaatsvinden. Maar als Sales.SalesOrderDetail bevat geen NULL ProductId s het verdubbelt het aantal benodigde zoekbewerkingen.



  1. Opgeslagen procedures schrijven voor professionele SSRS-rapporten

  2. Reden waarom orakel hoofdlettergevoelig is?

  3. MariaDB JSON_MERGE_PRESERVE() uitgelegd

  4. Active Data Guard Physical Standby instellen in RAC One Node Architecture - Deel 2