Différence entre EXISTS et IN en SQL?

443

Quelle est la différence entre la clause EXISTSet INdans SQL?

Quand devrions-nous utiliser EXISTSet quand devrions-nous utiliser IN?

Krantz
la source

Réponses:

224

Le existsmot-clé peut être utilisé de cette façon, mais il est vraiment destiné à éviter de compter:

--this statement needs to check the entire table
select count(*) from [table] where ...

--this statement is true as soon as one match is found
exists ( select * from [table] where ... )

C'est très utile lorsque vous avez des ifinstructions conditionnelles, car cela existspeut être beaucoup plus rapide que count.

Le inmieux est utilisé lorsque vous avez une liste statique à transmettre:

 select * from [table]
 where [field] in (1, 2, 3)

Lorsque vous avez un tableau dans une ininstruction, il est plus logique d'utiliser un join, mais surtout cela ne devrait pas avoir d'importance. L'optimiseur de requêtes doit renvoyer le même plan dans les deux cas. Dans certaines implémentations (généralement plus anciennes, telles que Microsoft SQL Server 2000), les inrequêtes obtiendront toujours un plan de jointure imbriqué , tandis que les joinrequêtes utiliseront imbriqué, fusionner ou hacher selon le cas. Des implémentations plus modernes sont plus intelligentes et peuvent ajuster le plan même lorsqu'il inest utilisé.

Keith
la source
2
Pourriez-vous élaborer sur "Lorsque vous avez une table dans une instruction in, il est plus judicieux d'utiliser une jointure, mais cela n'a pas vraiment d'importance. L'optimiseur de requête retournera le même plan dans les deux cas."? Pas la partie optimiseur de requête, la partie où vous pouvez utiliser un JOINen remplacement de IN.
farthVader
select * from [table] where [field] in (select [field] from [table2])renvoie les mêmes résultats (et plan de requête) que select * from [table] join [table2] on [table2].[field] = [table].[field].
@Sander ce n'est pas le cas: la première requête renvoie toutes les colonnes de table, tandis que la seconde renvoie tout de tableet table2. Dans certaines bases de données SQL (pour la plupart plus anciennes), la inrequête sera implémentée en tant que jointure imbriquée, tandis que la joinrequête peut être imbriquée, fusionnée, hachée, etc. - ce qui est le plus rapide.
Keith
2
D'accord, j'aurais dû spécifier des colonnes dans la clause select, mais vous devez mettre à jour votre réponse car elle indique clairement que les requêtes "renverront le même plan dans les deux cas".
existspeuvent être utilisés dans une déclaration de cas, ils peuvent donc être utiles de cette façon, c'estselect case when exists (select 1 from emp where salary > 1000) then 1 else 0 end as sal_over_1000
smooth_smoothie
125

EXISTSvous indiquera si une requête a renvoyé des résultats. par exemple:

SELECT * 
FROM Orders o 
WHERE EXISTS (
    SELECT * 
    FROM Products p 
    WHERE p.ProductNumber = o.ProductNumber)

IN est utilisé pour comparer une valeur à plusieurs, et peut utiliser des valeurs littérales, comme ceci:

SELECT * 
FROM Orders 
WHERE ProductNumber IN (1, 10, 100)

Vous pouvez également utiliser des résultats de requête avec la INclause, comme ceci:

SELECT * 
FROM Orders 
WHERE ProductNumber IN (
    SELECT ProductNumber 
    FROM Products 
    WHERE ProductInventoryQuantity > 0)
Matt Hamilton
la source
3
La dernière requête est dangereuse car elle peut échouer dans le cas où la sous-requête ne renvoie aucun résultat. La clause 'in' nécessite au moins 1 argument ...
user2054927
40
@ user2054927 La dernière requête ne retournera correctement aucune ligne si la sous-requête ne renvoie aucune ligne - rien de dangereux à ce sujet!
Tony Andrews
La meilleure réponse.
Aminadav Glickshtein
81

Basé sur l' optimiseur de règles :

  • EXISTSest beaucoup plus rapide que INlorsque les résultats de la sous-requête sont très importants.
  • INest plus rapide que EXISTSlorsque les résultats de la sous-requête sont très petits.

Basé sur l' optimiseur de coûts :

  • Il n'y a pas de différence.
jackson
la source
21
Une preuve de votre argument? Je ne pense pas que IN serait plus rapide que EXISTS jamais!
Nawaz
22
@Nawaz Et la preuve pourquoi IN est toujours plus lent qu'EXISTS?
ceving
2
Optimiseur de requête mal implémenté? J'ai l'impression que quelque chose comme ça (mais pas exactement cette situation) se produit dans certains RDBM ...
Haroldo_OK
1
EXISTS renvoie des valeurs purement booléennes, ce qui est toujours plus rapide que d'avoir à comparer des chaînes ou des valeurs supérieures à un type BIT / booléen. IN peut ou non être une comparaison booléenne. Étant donné que la programmation préfère l'utilisation EXPLICIT pour la stabilité (partie d'ACID), EXISTS est généralement préféré.
clifton_h
2
Pourquoi a-t-on voté autant de fois? Il n'y a absolument aucune raison pour que cette déclaration basée sur des hypothèses soit généralement vraie.
Lukas Eder
40

Je suppose que vous savez ce qu'ils font et que vous les utilisez donc différemment, je vais donc comprendre votre question comme suit: quand serait-ce une bonne idée de réécrire le SQL pour utiliser IN au lieu d'EXISTS, ou vice versa.

Est-ce une hypothèse juste?


Edit : La raison pour laquelle je demande est que dans de nombreux cas, vous pouvez réécrire un SQL basé sur IN pour utiliser un EXISTS à la place, et vice versa, et pour certains moteurs de base de données, l'optimiseur de requête traitera les deux différemment.

Par exemple:

SELECT *
FROM Customers
WHERE EXISTS (
    SELECT *
    FROM Orders
    WHERE Orders.CustomerID = Customers.ID
)

peut être réécrit en:

SELECT *
FROM Customers
WHERE ID IN (
    SELECT CustomerID
    FROM Orders
)

ou avec une jointure:

SELECT Customers.*
FROM Customers
    INNER JOIN Orders ON Customers.ID = Orders.CustomerID

Donc ma question est toujours d'actualité, est-ce que l'affiche originale se demande ce que fait IN et EXISTS, et donc comment l'utiliser, ou demande-t-il si la réécriture d'un SQL utilisant IN pour utiliser EXISTS à la place, ou vice versa, sera une bonne idée?

Lasse V. Karlsen
la source
12
Je ne connais pas l'OP, mais j'aimerais avoir la réponse à cette question! Quand dois-je utiliser EXISTS au lieu de IN avec une sous-requête qui renvoie des ID?
Roy Tinker
8
dans le JOIN, vous aurez besoin d'unDISTINCT
Jaider
4
grande démonstration, mais laisse à peu près la question sans réponse
Junchen Liu
28
  1. EXISTSest beaucoup plus rapide que INlorsque les résultats de la sous-requête sont très importants.
    INest plus rapide que EXISTSlorsque les résultats de la sous-requête sont très petits.

    CREATE TABLE t1 (id INT, title VARCHAR(20), someIntCol INT)
    GO
    CREATE TABLE t2 (id INT, t1Id INT, someData VARCHAR(20))
    GO
    
    INSERT INTO t1
    SELECT 1, 'title 1', 5 UNION ALL
    SELECT 2, 'title 2', 5 UNION ALL
    SELECT 3, 'title 3', 5 UNION ALL
    SELECT 4, 'title 4', 5 UNION ALL
    SELECT null, 'title 5', 5 UNION ALL
    SELECT null, 'title 6', 5
    
    INSERT INTO t2
    SELECT 1, 1, 'data 1' UNION ALL
    SELECT 2, 1, 'data 2' UNION ALL
    SELECT 3, 2, 'data 3' UNION ALL
    SELECT 4, 3, 'data 4' UNION ALL
    SELECT 5, 3, 'data 5' UNION ALL
    SELECT 6, 3, 'data 6' UNION ALL
    SELECT 7, 4, 'data 7' UNION ALL
    SELECT 8, null, 'data 8' UNION ALL
    SELECT 9, 6, 'data 9' UNION ALL
    SELECT 10, 6, 'data 10' UNION ALL
    SELECT 11, 8, 'data 11'
  2. Requête 1

    SELECT
    FROM    t1 
    WHERE   not  EXISTS (SELECT * FROM t2 WHERE t1.id = t2.t1id)

    Requête 2

    SELECT t1.* 
    FROM   t1 
    WHERE  t1.id not in (SELECT  t2.t1id FROM t2 )

    Si t1votre identifiant a une valeur nulle, la requête 1 les trouvera, mais la requête 2 ne trouvera pas de paramètres nuls.

    Je veux dire INne peut rien comparer avec null, donc il n'a aucun résultat pour null, mais EXISTSpeut tout comparer avec null.

Alireza Masali
la source
Cette réponse est un synopsis raisonnable du sentiment de Tom Kite ( asktom.oracle.com/pls/asktom/… )
Jeromy French
Je pense que cette réponse est basée sur l'intuition, ce qui est assez juste. Mais cela ne peut pas être universellement vrai. Par exemple, ce n'est presque certainement pas le cas d' Ingres , qui analyserait les deux requêtes SQL équivalentes pour être la même requête QUEL, qui n'a pas la `` richesse '' de SQL - ahem - quand il s'agit d'écrire la même chose de plusieurs manières.
onedaywhen
Ces 2 requêtes sont logiquement équivalentes si et seulement si t2.id est défini comme "NOT NULL". Pour garantir l'équivalence sans dépendance dans la définition de la table, la deuxième requête doit être "SELECT t1. * FROM t1 WHERE t1.id not in (SELECT t2.id FROM t2 where t2.id is not null )"
David דודו Markovitz
16

Si vous utilisez l' INopérateur, le moteur SQL analysera tous les enregistrements extraits de la requête interne. D'un autre côté, si nous utilisons EXISTS, le moteur SQL arrêtera le processus d'analyse dès qu'il aura trouvé une correspondance.

Si vous utilisez l'opérat IN
la source
10

IN prend uniquement en charge les relations d'égalité (ou l'inégalité lorsqu'elle est précédée de NOT ).
C'est un synonyme de = any / = some , eg

select    * 
from      t1 
where     x in (select x from t2)
;

EXISTS prend en charge différents types de relations, qui ne peuvent pas être exprimées à l'aide de IN , par exemple -

select    * 
from      t1 
where     exists (select    null 
                  from      t2 
                  where     t2.x=t1.x 
                        and t2.y>t1.y 
                        and t2.z like '℅' || t1.z || '℅'
                  )
;

Et sur une note différente -

Les prétendues performances et les différences techniques entre EXISTS et IN peuvent résulter d'implémentations / limitations / bogues spécifiques de fournisseurs, mais bien souvent, ce ne sont que des mythes créés en raison du manque de compréhension des bases de données internes.

La définition des tables, la précision des statistiques, la configuration de la base de données et la version de l'optimiseur ont tous un impact sur le plan d'exécution et donc sur les mesures de performance.

David דודו Markovitz
la source
Votez pour votre commentaire sur les performances: sans nous concentrer sur un SGBD spécifique, nous devons supposer que c'est à l'optimiseur de déterminer ce qui fonctionne le mieux.
Manngo
9

Le Existsmot clé évalue vrai ou faux, mais le INmot clé compare toutes les valeurs dans la colonne de sous-requête correspondante. Un autre Select 1peut être utilisé avec la Existscommande. Exemple:

SELECT * FROM Temp1 where exists(select 1 from Temp2 where conditions...)

Mais INest moins efficace donc Existsplus rapide.

Arulraj.M
la source
5

Je pense,

  • EXISTSc'est lorsque vous devez faire correspondre les résultats de la requête avec une autre sous-requête. Les résultats de la requête n ° 1 doivent être récupérés là où les résultats de la sous-requête correspondent. Type de jointure. Par exemple, sélectionnez le tableau des clients n ° 1 qui a également passé le tableau des commandes n ° 2

  • IN est à récupérer si la valeur d'une colonne spécifique se trouve dans INune liste (1, 2, 3, 4, 5).

Quand utiliser l'un par rapport à l'autre ... quand vous sentez qu'il se lit correctement (communique mieux l'intention).

Gishu
la source
4

La différence réside ici:

select * 
from abcTable
where exists (select null)

La requête ci-dessus renverra tous les enregistrements tandis que celle ci-dessous retournera vide.

select *
from abcTable
where abcTable_ID in (select null)

Essayez-le et observez la sortie.

garçon voyou
la source
1
Hmmm ... Erreur: [SQL0104] Token) n'était pas valide. Dans les deux cas. Supposez-vous un SGBDR particulier?
jmarkmurphy
3

Selon ma connaissance, lorsqu'une sous-requête renvoie une NULLvaleur, alors l'instruction entière devient NULL. Dans ce cas, nous utilisons le EXITSmot - clé. Si nous voulons comparer des valeurs particulières dans des sous-requêtes, nous utilisons le INmot - clé.

RAM
la source
3

Laquelle est la plus rapide dépend du nombre de requêtes récupérées par la requête interne:

  • Lorsque votre requête interne récupérant des milliers de lignes, EXIST serait un meilleur choix
  • Lorsque votre requête interne récupérant quelques lignes, IN sera plus rapide

EXIST évalue sur vrai ou faux mais IN compare plusieurs valeurs. Lorsque vous ne savez pas que l'enregistrement existe ou non, vous devez choisir EXIST.

Sumair Hussain Rajput
la source
3

La raison en est que l'opérateur EXISTS fonctionne selon le principe «au moins trouvé». Il retourne vrai et arrête l'analyse du tableau une fois qu'au moins une ligne correspondante a été trouvée.

D'autre part, lorsque l'opérateur IN est combiné avec une sous-requête, MySQL doit d'abord traiter la sous-requête, puis utilise le résultat de la sous-requête pour traiter la requête entière.

La règle générale est que si la sous-requête contient un grand volume de données, l'opérateur EXISTS offre de meilleures performances.

Cependant, la requête qui utilise l'opérateur IN s'exécutera plus rapidement si l'ensemble de résultats renvoyé par la sous-requête est très petit.

Vipin Jain
la source
1

Ma compréhension est que les deux devraient être les mêmes tant que nous n'avons pas affaire à des valeurs NULL.

La même raison pour laquelle la requête ne renvoie pas la valeur pour = NULL vs est NULL. http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/

En ce qui concerne l'argument booléen vs comparateur, pour générer un booléen, les deux valeurs doivent être comparées et c'est ainsi que fonctionne une condition, donc je n'arrive pas à comprendre comment IN et EXISTS se comportent différemment.

Ranjeeth
la source
0

Si une sous-requête renvoie plusieurs valeurs, vous devrez peut-être exécuter la requête externe - si les valeurs de la colonne spécifiée dans la condition correspondent à n'importe quelle valeur du jeu de résultats de la sous-requête. Pour effectuer cette tâche, vous devez utiliser le inmot - clé.

Vous pouvez utiliser une sous-requête pour vérifier si un ensemble d'enregistrements existe. Pour cela, vous devez utiliser la existsclause avec une sous-requête. Le existsmot clé renvoie toujours une valeur vraie ou fausse.

djohn
la source
0

Je crois que cela a une réponse simple. Pourquoi ne le vérifiez-vous pas auprès des personnes qui ont développé cette fonction dans leurs systèmes?

Si vous êtes un développeur MS SQL, voici la réponse directement de Microsoft.

IN:

Détermine si une valeur spécifiée correspond à une valeur d'une sous-requête ou d'une liste.

EXISTS:

Spécifie une sous-requête pour tester l'existence de lignes.

Erreur fatale
la source
0

J'ai trouvé que l'utilisation du mot clé EXISTS est souvent très lente (c'est très vrai dans Microsoft Access). J'utilise plutôt l'opérateur de jointure de cette manière: devrais-je-utiliser-le-mot-clé-existe-dans-sql

Axel Der
la source
-1

EXISTS est plus rapide en performances qu'en IN. Si la plupart des critères de filtrage sont dans la sous-requête, il vaut mieux utiliser IN et si la plupart des critères de filtrage sont dans la requête principale, il vaut mieux utiliser EXISTS.

Deva
la source
Cette affirmation n'est vraiment étayée par aucune preuve, n'est-ce pas?
Lukas Eder
-2

Si vous utilisez l'opérateur IN, le moteur SQL analysera tous les enregistrements extraits de la requête interne. D'un autre côté, si nous utilisons EXISTS, le moteur SQL arrêtera le processus d'analyse dès qu'il trouvera une correspondance.

Gagandeep Singh
la source
@ziggy expliquer? C'est à peu près ce que dit également la réponse acceptée. Dans DOIT vérifier chaque enregistrement, il peut s'arrêter dès qu'il en trouve un seul.
Ben Thurley
Non, pas correct. INet EXISTSpeuvent être équivalents et transformés les uns dans les autres.
Lukas Eder