Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

NOT IN срещу NOT EXISTS

Винаги по подразбиране е NOT EXISTS .

Плановете за изпълнение може да са същите в момента, но ако някоя от колоните бъде променена в бъдеще, за да позволи NULL е NOT IN версията ще трябва да свърши повече работа (дори ако няма NULL s всъщност присъстват в данните) и семантиката на NOT IN ако NULL с са настоящето е малко вероятно да са тези, които искате.

Когато нито Products.ProductID или [Order Details].ProductID разреши NULL е NOT IN ще бъдат третирани по същия начин като следната заявка.

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

Точният план може да варира, но за моите примерни данни получавам следното.

Доста често срещано погрешно схващане изглежда е, че корелираните подзаявки винаги са "лоши" в сравнение с присъединяванията. Те със сигурност могат да бъдат, когато налагат план за вложени цикли (подзаявка се оценява ред по ред), но този план включва логически оператор против полуприсъединяване. Анти-полусъединяванията не са ограничени до вложени цикли, но могат да използват и хеш или сливане (както в този пример).

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

Ако [Order Details].ProductID е NULL -може заявката след това става

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) 

Причината за това е, че правилната семантика if [Order Details] съдържа произволен NULL ProductId s е да не връща резултати. Вижте допълнителната шпула за анти-полусъединяване и броене на редове, за да проверите това, което е добавено към плана.

Ако Products.ProductID също се променя, за да стане NULL -може заявката след това става

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) 

Причината за това е, че NULL Products.ProductId не трябва да се връща в резултатите освен ако NOT IN подзаявката трябваше да не върне никакви резултати (т.е. [Order Details] масата е празна). В този случай трябва. В плана за моите примерни данни това е реализирано чрез добавяне на друго анти полусъединяване, както е по-долу.

Ефектът от това е показан в публикацията в блога, вече свързана от Бъкли. В примера броят на логическите четения се увеличава от около 400 на 500 000.

Освен това фактът, че единичен NULL може да намали броя на редовете до нула прави оценката на мощността много трудна. Ако SQL Server предполага, че това ще се случи, но всъщност нямаше NULL редове в данните, останалата част от плана за изпълнение може да бъде катастрофално по-лоша, ако това е само част от по-голяма заявка, с неподходящи вложени цикли, причиняващи многократно изпълнение на скъпо поддърво, например.

Това не е единственият възможен план за изпълнение за NOT IN на NULL -възможна колона обаче. Тази статия показва още една за заявка срещу AdventureWorks2008 база данни.

За NOT IN на NOT NULL колона или NOT EXISTS срещу колона с нула или без нула дава следния план.

Когато колоната се промени на NULL -възможност NOT IN сега планът изглежда така

Той добавя допълнителен оператор за вътрешно присъединяване към плана. Това устройство е обяснено тук. Всичко е там, за да преобразува предишния единичен корелиран индекс за търсене в Sales.SalesOrderDetail.ProductID = <correlated_product_id> до две търсения на външен ред. Допълнителният е на WHERE Sales.SalesOrderDetail.ProductID IS NULL .

Тъй като това е под анти полусъединяване, ако това върне някакви редове, второто търсене няма да се случи. Ако обаче Sales.SalesOrderDetail не съдържа NULL ProductID s това ще удвои броя на необходимите операции за търсене.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Node.js MSSQL tedius ConnectionError:Неуспешно свързване към localhost:1433 - свържете ECONNREFUSED

  2. Как да настроите Spotlight Cloud и ефективно да отстраните неизправности на SQL сървър

  3. SQL MAX от множество колони?

  4. Как да получите първата и последната дата на текущата година?

  5. Как да съпоставите множество дялове в една файлова група в SQL Server (T-SQL)