Ne devrait-on PAS éviter?

14

Parmi certains développeurs SQL Server, c'est une croyance largement répandue qui NOT INest terriblement lente , et les requêtes doivent être réécrites afin qu'elles renvoient le même résultat mais n'utilisent pas les mots clés "mauvais". ( exemple ).

Y a-t-il une vérité à cela?

Existe-t-il, par exemple, un bogue connu dans SQL Server (quelle version?) Qui fait que les requêtes utilisant NOT INont un plan d'exécution pire qu'une requête équivalente qui utilise

  • un LEFT JOINcombiné avec un NULLchèque ou
  • (SELECT COUNT(*) ...) = 0dans la WHEREclause?
Heinzi
la source
7
Cet article est cependant très inexact. "In" ne doit pas "exécuter la même requête encore et encore pour chaque ligne de TableOne". L'affiche semble croire que IN/ NOT INsera toujours implémenté avec des boucles imbriquées. Et je n'ai aucune idée de ce qui stops SQL Server from creating a ‘plan’est censé signifier.
Martin Smith
5
@Heinzi Cet article auquel vous vous connectez, devrait mourir dans le feu, il est plein de bêtises. Comme: "Pour remplacer IN, nous utilisons un INNER JOIN. Ce sont en fait la même chose." Le problème, c'est que ce n'est pas la même chose. Je ne ferais pas confiance à quelqu'un qui ne connaît pas le SQL de base, c'est-à-dire la différence entre une jointure et une semi-jointure, pour analyser quoi que ce soit sur le comportement de SQL-Server.
ypercubeᵀᴹ

Réponses:

14

Je ne pense pas que cela ait à voir avec le fait d'être terriblement lent; cela a à voir avec le fait d'être potentiellement inexact. Par exemple, compte tenu des données suivantes - commandes pouvant être passées soit par un client individuel, soit par un partenaire 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);

Disons que je veux trouver tous les clients qui n'ont jamais passé de commande. Compte tenu des données, il n'y en a qu'un: le client n ° 2. Voici trois façons de rédiger une requête pour trouver ces informations (il y en a d'autres):

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;

Résultats:

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

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

EXCEPT
------
2

Maintenant, il y a aussi des problèmes de performances, et j'en parle dans ce blog . En fonction des données et des index, NOT EXISTSil surclasse généralement les performances NOT IN, et je ne sais pas si cela pourrait se dégrader. Vous devez également noter que cela EXCEPTpeut introduire une opération de tri distincte, vous pouvez donc vous retrouver avec des données différentes (encore une fois, selon la source). Et que le LEFT OUTER JOIN ... WHERE right.column IS NULLmodèle populaire est toujours le moins performant.

Martin Smith a également beaucoup de bonnes informations à l'appui dans sa réponse sur SO .

Aaron Bertrand
la source