Czy NIE należy unikać?

14

Wśród niektórych deweloperów programu SQL Server powszechnie NOT INpanuje przekonanie, które jest strasznie powolne , a zapytania powinny być przepisywane tak, aby zwracały ten sam wynik, ale nie używały słów kluczowych „zło”. ( przykład ).

Czy jest w tym jakaś prawda?

Czy istnieje na przykład jakiś znany błąd w SQL Server (która wersja?), Który powoduje, że zapytania korzystają NOT INz gorszego planu wykonania niż równoważne zapytanie, które używa

  • w LEFT JOINpołączeniu z NULLczekiem lub
  • (SELECT COUNT(*) ...) = 0w WHEREklauzuli?
Heinzi
źródło
7
Ten artykuł jest jednak bardzo niedokładny. „In” nie musi uruchamiać tego samego zapytania w kółko dla każdego wiersza w TableOne ”. Plakat wydaje się wierzyć, że IN/ NOT INzawsze będzie realizowany za pomocą zagnieżdżonych pętli. I nie mam pojęcia, co stops SQL Server from creating a ‘plan’to znaczy.
Martin Smith,
5
@Heinzi Ten artykuł, do którego prowadzi link, powinien zginąć w ogniu, jest pełen nonsensów. Na przykład: „Aby zastąpić IN, używamy WEJŚCIA WEWNĘTRZNEGO. Są to w rzeczywistości to samo”. Problem w tym, że to nie to samo. Nie ufałbym komuś, kto nie zna podstawowego SQL, tj. Różnicy między łączeniem a łączeniem częściowym, aby analizował cokolwiek na temat zachowania SQL-Server.
ypercubeᵀᴹ

Odpowiedzi:

14

Nie sądzę, żeby miało to coś wspólnego z byciem strasznie wolnym; ma to związek z potencjalnym niedokładnością. Na przykład, biorąc pod uwagę następujące dane - zamówienia, które mogą być składane przez klienta indywidualnego lub partnera B2B:

DECLARE @Customers TABLE(CustomerID INT);

INSERT @Customers VALUES(1),(2);

DECLARE @Orders TABLE(OrderID INT, CustomerID INT, CompanyID INT);

INSERT @Orders VALUES(10,1,NULL),(11,NULL,5);

Powiedzmy, że chcę znaleźć wszystkich klientów, którzy nigdy nie złożyli zamówienia. Biorąc pod uwagę dane, jest tylko jeden: klient nr 2. Oto trzy sposoby pisania zapytania w celu znalezienia tych informacji (są inne):

SELECT [NOT IN] = CustomerID FROM @Customers 
  WHERE CustomerID NOT IN (SELECT CustomerID FROM @Orders);

SELECT [NOT EXISTS] = CustomerID FROM @Customers AS c 
  WHERE NOT EXISTS (SELECT 1 FROM @Orders AS o
  WHERE o.CustomerID = c.CustomerID);

SELECT [EXCEPT] = CustomerID FROM @Customers
EXCEPT SELECT CustomerID FROM @Orders;

Wyniki:

NOT IN
------
                 -- <-- no results. Is that what you expected?

NOT EXISTS
----------
2

EXCEPT
------
2

Teraz są też problemy z wydajnością i mówię o nich w tym poście na blogu . W zależności od danych i indeksów NOT EXISTSzwykle osiągają lepsze wyniki NOT IN, a ja nie wiem, czy może kiedykolwiek gorzej. Należy również pamiętać, że EXCEPTmożna wprowadzić odrębną operację sortowania, co może skutkować uzyskaniem różnych danych (ponownie, w zależności od źródła). I że popularny LEFT OUTER JOIN ... WHERE right.column IS NULLwzór jest zawsze najgorszy.

Martin Smith ma również wiele dobrych informacji pomocniczych w swojej odpowiedzi na temat SO .

Aaron Bertrand
źródło