Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

NIE W vs NIE ISTNIEJE

Zawsze domyślnie NOT EXISTS .

Plany wykonania mogą być w tej chwili takie same, ale jeśli którakolwiek kolumna zostanie zmieniona w przyszłości, aby umożliwić NULL s NOT IN wersja będzie musiała wykonać więcej pracy (nawet jeśli nie ma NULL s są faktycznie obecne w danych) i semantyki NOT IN jeśli NULL teraźniejszość i tak prawdopodobnie nie będą tymi, których chcesz.

Gdy żaden Products.ProductID lub [Order Details].ProductID zezwól na NULL s NOT IN zostanie potraktowany identycznie jak następujące zapytanie.

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

Dokładny plan może się różnić, ale dla moich przykładowych danych otrzymuję następujące.

Wydaje się, że dość powszechnym błędnym przekonaniem jest to, że skorelowane zapytania podrzędne są zawsze „złe” w porównaniu do złączeń. Z pewnością mogą być, gdy wymuszają plan zagnieżdżonych pętli (podkwerenda oceniana wiersz po wierszu), ale ten plan zawiera operator logiczny zapobiegający sprzężeniu semi. Anti semijoin nie ogranicza się do zagnieżdżonych pętli, ale może również używać skrótów lub łączeń scalających (jak w tym przykładzie).

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

Jeśli [Order Details].ProductID jest NULL -able zapytanie staje się wtedy

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) 

Powodem tego jest to, że poprawna semantyka, jeśli [Order Details] zawiera dowolny NULL ProductId s nie zwraca żadnych wyników. Zobacz dodatkową szpulę anty-semi-join i licznika rzędów, aby sprawdzić, czy jest to dodane do planu.

Jeśli Products.ProductID zmienia się również na NULL -able zapytanie staje się wtedy

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) 

Powodem tego jest to, że NULL Products.ProductId nie powinny być zwracane w wynikach z wyjątkiem jeśli NOT IN zapytanie podrzędne miało nie zwracać żadnych wyników (np. [Order Details] tabela jest pusta). W takim razie powinno. W planie dla moich przykładowych danych jest to zaimplementowane przez dodanie kolejnego anty-semijoin, jak poniżej.

Efekt tego widać w poście na blogu, do którego już link Buckley. W tym przykładzie liczba odczytów logicznych wzrosła z około 400 do 500 000.

Dodatkowo fakt, że pojedynczy NULL może zmniejszyć liczbę wierszy do zera, co bardzo utrudnia oszacowanie kardynalności. Jeśli SQL Server zakłada, że ​​tak się stanie, ale w rzeczywistości nie było NULL wierszy w danych, reszta planu wykonania może być katastrofalnie gorsza, jeśli jest to tylko część większego zapytania, z nieodpowiednimi pętlami zagnieżdżonymi powodującymi powtarzające się wykonywanie kosztownego poddrzewa.

To nie jedyny możliwy plan wykonania dla NOT IN na NULL -możliwa kolumna jednak. W tym artykule przedstawiono kolejne zapytanie dotyczące AdventureWorks2008 baza danych.

Dla NOT IN na NOT NULL kolumna lub NOT EXISTS w stosunku do kolumny z wartością null lub nie do wartości null daje następujący plan.

Gdy kolumna zmieni się na NULL -może NOT IN plan wygląda teraz tak

Dodaje do planu dodatkowy operator sprzężenia wewnętrznego. To urządzenie zostało wyjaśnione tutaj. Wszystko po to, aby przekonwertować poprzednie pojedyncze wyszukiwanie indeksu skorelowanego na Sales.SalesOrderDetail.ProductID = <correlated_product_id> do dwóch wyszukiwań w zewnętrznym rzędzie. Dodatkowa znajduje się na WHERE Sales.SalesOrderDetail.ProductID IS NULL .

Ponieważ jest to pod anty semi join, jeśli zwróci jakiekolwiek wiersze, drugie wyszukiwanie nie nastąpi. Jeśli jednak Sales.SalesOrderDetail nie zawiera żadnego NULL ProductId s podwoi liczbę wymaganych operacji wyszukiwania.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Scal dwie partycje w jedną w programie SQL Server (T-SQL)

  2. 5 szybkich przydatnych wskazówek dla administratorów baz danych SQL Server Production

  3. Obejście dla DATEDIFF() Ignorowanie SET DATEFIRST w SQL Server (przykład T-SQL)

  4. 6 problematycznych zapytań, które znacznie spowalniają Twoją bazę danych

  5. LPAD w SQL Server 2008