2009-08-31 9 views
11

Jaki jest najskuteczniejszy sposób napisania instrukcji select podobnej do poniższej.Jaki jest najskuteczniejszy sposób zapisu instrukcji select z podzapytaniem "not in"?

SELECT * 
FROM Orders 
WHERE Orders.Order_ID not in (Select Order_ID FROM HeldOrders) 

Istotą jest to, że chcesz zapisywać dane z jednej tabeli, gdy element nie znajduje się w innej tabeli.

+5

Najlepszym sposobem jest wypróbowanie różnych podejść i zbadanie planów wykonania. – pjp

+0

W mojej sytuacji SQL Server 2000, biorąc pod uwagę indeksy w tabelach, zapytanie "Dołącz" było najszybsze. SELECT * zleceń ö lewo Dołącz HeldOrders h na o.Order_ID = h.Order_ID i h.Order_ID jest zerowy – Stimy

Odpowiedz

7

„Najbardziej skuteczny” będzie różna w zależności od wielkości tabel , indeksy i tak dalej. Innymi słowy, będzie się różnić w zależności od konkretnego przypadku, którego używasz.

Są trzy sposoby, których zwykle używam, aby osiągnąć to, co chcesz, w zależności od sytuacji.

1. Twój przykład działa poprawnie, jeśli Order.order_id jest indeksowany, a HeldOrders jest dość mały.

2. Innym sposobem jest „podzapytanie skorelowane”, która jest niewielka zmienność, co masz ...

SELECT * 
FROM Orders o 
WHERE Orders.Order_ID not in (Select Order_ID 
           FROM HeldOrders h 
           where h.order_id = o.order_id) 

Uwaga dodanie klauzuli WHERE. To działa lepiej, gdy HeldOrders ma dużą liczbę rzędów. Identyfikator zamówienia musi być indeksowany w obu tabelach.

3. Inna metoda używam czasami pozostaje sprzężenia zewnętrznego ...

SELECT * 
FROM Orders o 
left outer join HeldOrders h on h.order_id = o.order_id 
where h.order_id is null 

Podczas korzystania z LEFT OUTER JOIN, h.order_id będzie mieć wartość w to dopasowanie o.order_id gdy istnieje pasujący wiersz. Jeśli nie ma pasującego wiersza, h.order_id będzie mieć wartość NULL. Sprawdzając wartości NULL w klauzuli where, możesz filtrować wszystko, co nie pasuje.

Każda z tych odmian może działać mniej lub bardziej wydajnie w różnych sytuacjach.

+0

'@ Dave': dlaczego używasz' NOT IN' zamiast 'NOT EXISTS' w metodzie' 2'? – Quassnoi

+1

@Quassnoi: Szczerze, prawdopodobnie zły nawyk. Po przeczytaniu powyższej odpowiedzi zamierzam zacząć korzystać z NOT EXISTS. –

+0

Opcja 3 działała najlepiej w moim scenariuszu (SQL Server 2000 ze względu na moje indeksy tabel). Myślę, że najlepszą odpowiedzią jest przetestowanie wielu metod. – Stimy

4

Możesz użyć LEFT OUTER JOIN i sprawdzić NULL na prawej tabeli.

SELECT O1.* 
FROM Orders O1 
LEFT OUTER JOIN HeldOrders O2 
ON O1.Order_ID = O2.Order_Id 
WHERE O2.Order_Id IS NULL 
+1

to * * daleko od bycia najbardziej skuteczny sposób. – Quassnoi

+0

To niekoniecznie będzie najbardziej wydajną metodą. –

+0

Jest jednak znacznie bardziej efektywny niż pod-zapytanie - przynajmniej wykonuje się tylko w stosunku do drugiej tabeli, zamiast raz/wiersz. – SqlRyan

19

Na początek link do starego artykułu na moim blogu na jak NOT IN orzecznik działa w SQL Server (iw innych układach zbyt):


Możesz przepisać go w następujący sposób:

SELECT * 
FROM Orders o 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM HeldOrders ho 
     WHERE ho.OrderID = o.OrderID 
     ) 

, jednak większość baz danych będzie traktować te zapytania w taki sam sposób.

Oba te zapytania będą używać pewnego rodzaju ANTI JOIN.

Jest to przydatne dla SQL Server jeśli chcesz sprawdzić dwie lub więcej kolumn, ponieważ SQL Server nie obsługuje tej składni:

SELECT * 
FROM Orders o 
WHERE (col1, col2) NOT IN 
     (
     SELECT col1, col2 
     FROM HeldOrders ho 
     ) 

jednak pamiętać, że NOT IN może być trudne ze względu na sposób, w jaki traktuje NULL wartości.

Jeśli Held.Orders jest pustych, żadne zapisy znajdują się i powraca podzapytanie tylko jeden NULL cała kwerenda zwróci nic (zarówno IN i NOT IN oceni do NULL w tym przypadku).

Rozważmy te dane:

Orders: 

OrderID 
--- 
1 

HeldOrders: 

OrderID 
--- 
2 
NULL 

tego zapytania:

SELECT * 
FROM Orders o 
WHERE OrderID NOT IN 
     (
     SELECT OrderID 
     FROM HeldOrders ho 
     ) 

powróci nic, który prawdopodobnie nie jest to, czego można oczekiwać.

Jednakże tego:

SELECT * 
FROM Orders o 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM HeldOrders ho 
     WHERE ho.OrderID = o.OrderID 
     ) 

powróci wierszach OrderID = 1.

Należy pamiętać, że rozwiązania proponowane przez innych użytkowników nie są najskuteczniejszym rozwiązaniem.

To zapytanie:

SELECT * 
FROM Orders o 
LEFT JOIN 
     HeldOrders ho 
ON  ho.OrderID = o.OrderID 
WHERE ho.OrderID IS NULL 

użyje warunek filtru, które będą potrzebne do oceny i odfiltrować wszystkie dopasowanie wiersze, które mogą być numerius

ANTI JOIN metoda stosowana zarówno przez IN i EXISTS będzie wystarczy upewnić się, że rekord nie istnieje po dla każdego wiersza w Orders, więc najpierw wyeliminuje wszystkie możliwe duplikaty:

  • NESTED LOOPS ANTI JOIN i MERGE ANTI JOIN po prostu pominie duplikaty podczas oceny HeldOrders.
  • A HASH ANTI JOIN wyeliminuje duplikaty podczas budowania tabeli skrótów.
+1

Po raz pierwszy widziałem skorelowane podkwerendy, które faktycznie musiały być skorelowane podkwerendy, które mogłem grok w mniej niż 5 minut. Chciałbym znać tę sztuczkę * lata temu *. –

+0

'@Philip Kelley': która sztuczka dokładnie? – Quassnoi

+0

Co masz na myśli w tej sekcji: "Jest to przydatne dla SQL Server, jeśli chcesz sprawdzić dwie lub więcej kolumn, ponieważ SQL Server nie obsługuje tej składni:". Czy mówisz, że to nie dotyczy SQL Server? Czy brakuje Ci "nie"? – Stimy

1

Nie jestem pewien, co jest najbardziej wydajny, ale inne opcje:

1. Use EXISTS 

SELECT * 
FROM ORDERS O 
WHERE NOT EXISTS (SELECT 1 
        FROM HeldOrders HO 
        WHERE O.Order_ID = HO.OrderID) 

2. Use EXCEPT 

SELECT O.Order_ID 
FROM ORDERS O 
EXCEPT 
SELECT HO.Order_ID 
FROM HeldOrders 
0

Spróbuj

SELECT * 
FROM Orders 
LEFT JOIN HeldOrders 
ON HeldOrders.Order_ID = Orders.Order_ID 
WHERE HeldOrders.Order_ID IS NULL 
Powiązane problemy