NOT IN dovrebbe essere evitato?


14

Tra alcuni sviluppatori di SQL Server, è una convinzione diffusa che NOT INè terribilmente lenta e le query dovrebbero essere riscritte in modo da restituire lo stesso risultato ma non utilizzare le parole chiave "malvagie". ( esempio ).

C'è qualche verità in questo?

Esiste, ad esempio, qualche bug noto in SQL Server (quale versione?) Che fa sì che le query utilizzino NOT INun piano di esecuzione peggiore rispetto a una query equivalente che utilizza

  • un LEFT JOINcombinato con un NULLassegno o
  • (SELECT COUNT(*) ...) = 0nella WHEREclausola?

7
Quell'articolo però è altamente impreciso. "In" non "deve eseguire ripetutamente la stessa query per ogni riga in TableOne". Il poster sembra credere che IN/ NOT INsarà sempre implementato con anelli annidati. E non ho idea di cosa stops SQL Server from creating a ‘plan’dovrebbe significare.
Martin Smith,

5
@Heinzi L'articolo a cui ti colleghi, dovrebbe morire nel fuoco, è pieno di sciocchezze. Come: "Per sostituire IN, usiamo un INNER JOIN. Sono effettivamente la stessa cosa". Il problema è che non sono la stessa cosa. Non mi fiderei di qualcuno che non conosca l'SQL di base, ovvero la differenza tra un join e un semi-join, per analizzare qualsiasi cosa sul comportamento di SQL Server.
ypercubeᵀᴹ

Risposte:


14

Non penso che abbia nulla a che fare con l'essere terribilmente lento; ha a che fare con l'essere potenzialmente inaccurati. Ad esempio, dati i seguenti dati - ordini che potrebbero essere effettuati da un singolo cliente o da un partner 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);

Diciamo che voglio trovare tutti i clienti che non hanno mai effettuato un ordine. Dati i dati, ce n'è solo uno: cliente n. 2. Ecco tre modi in cui potrei fare per scrivere una query per trovare quelle informazioni (ce ne sono altre):

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;

risultati:

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

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

EXCEPT
------
2

Ora, ci sono anche alcuni problemi di prestazioni, e ne parlo in questo post sul blog . A seconda dei dati e degli indici, di NOT EXISTSsolito sovraperformerà NOT INe non so se potrebbe mai andare peggio. Dovresti anche notare che EXCEPTpuoi introdurre un'operazione di ordinamento distinta, quindi potresti finire con dati diversi (di nuovo, a seconda della fonte). E che il LEFT OUTER JOIN ... WHERE right.column IS NULLmodello popolare è sempre il peggiore.

Martin Smith ha anche molte buone informazioni di supporto nella sua risposta su SO .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.