web-dev-qa-db-fra.com

EXISTS vs JOIN et utilisation de la clause EXISTS

Voici l'exemple de code:

CREATE TABLE #titles(
    title_id       varchar(20),
    title          varchar(80)       NOT NULL,
    type           char(12)          NOT NULL,
    pub_id         char(4)               NULL,
    price          money                 NULL,
    advance        money                 NULL,
    royalty        int                   NULL,
    ytd_sales      int                   NULL,
    notes          varchar(200)          NULL,
    pubdate        datetime          NOT NULL
 )
 GO

 insert #titles values ('1', 'Secrets',   'popular_comp', '1389', $20.00, $8000.00, 10, 4095,'Note 1','06/12/94')
 insert #titles values ('2', 'The',       'business',     '1389', $19.99, $5000.00, 10, 4095,'Note 2','06/12/91')
 insert #titles values ('3', 'Emotional', 'psychology',   '0736', $7.99,  $4000.00, 10, 3336,'Note 3','06/12/91')
 insert #titles values ('4', 'Prolonged', 'psychology',   '0736', $19.99, $2000.00, 10, 4072,'Note 4','06/12/91')
 insert #titles values ('5', 'With',      'business',     '1389', $11.95, $5000.00, 10, 3876,'Note 5','06/09/91')
 insert #titles values ('6', 'Valley',    'mod_cook',     '0877', $19.99, $0.00,    12, 2032,'Note 6','06/09/91')
 insert #titles values ('7', 'Any?',      'trad_cook',    '0877', $14.99, $8000.00, 10, 4095,'Note 7','06/12/91')
 insert #titles values ('8', 'Fifty',     'trad_cook',    '0877', $11.95, $4000.00, 14, 1509,'Note 8','06/12/91')
 GO


CREATE TABLE #sales(
    stor_id        char(4)           NOT NULL,
    ord_num        varchar(20)       NOT NULL,
    ord_date       datetime          NOT NULL,
    qty            smallint          NOT NULL,
    payterms       varchar(12)       NOT NULL,
    title_id       varchar(80)
)
 GO
insert #sales values('1', 'QA7442.3', '09/13/94', 75, 'ON Billing','1')
insert #sales values('2', 'D4482',    '09/14/94', 10, 'Net 60',    '1')
insert #sales values('3', 'N914008',  '09/14/94', 20, 'Net 30',    '2')
insert #sales values('4', 'N914014',  '09/14/94', 25, 'Net 30',    '3')
insert #sales values('5', '423LL922', '09/14/94', 15, 'ON Billing','3')
insert #sales values('6', '423LL930', '09/14/94', 10, 'ON Billing','2')


SELECT    title, price
FROM      #titles
WHERE     EXISTS
(SELECT   *
FROM      #sales
WHERE     #sales.title_id = #titles.title_id
AND       qty >30)


    SELECT    t.title, t.price
    FROM     #titles t
    inner join #sales s on t.title_id = s.title_id
    where s.qty >30 

Je veux savoir quelle est la différence entre les 2 requêtes ci-dessus qui donne le même résultat. Je veux également savoir à quoi sert le mot-clé EXISTS et où exactement l'utiliser?

48
satyajit

EXISTS est utilisé pour renvoyer une valeur booléenne, JOIN renvoie une toute autre table

EXISTS est uniquement utilisé pour tester si une sous-requête renvoie des résultats et court-circuite dès qu'elle le fait. JOIN est utilisé pour étendre un jeu de résultats en le combinant avec des champs supplémentaires d'une autre table à laquelle il existe une relation.

Dans votre exemple, les requêtes sont symantiquement équivalentes.

En général, utilisez EXISTS lorsque:

  • Vous n'avez pas besoin de renvoyer les données de la table associée
  • Vous avez des dupes dans la table associée (JOIN peut provoquer des doublons de lignes si les valeurs sont répétées)
  • Vous voulez vérifier l'existence (utilisez au lieu de LEFT OUTER JOIN...NULL condition)

Si vous disposez d'index appropriés, la plupart du temps, le EXISTS fonctionnera de manière identique au JOIN. L'exception concerne les sous-requêtes très compliquées, où il est normalement plus rapide d'utiliser EXISTS.

Si votre clé JOIN n'est pas indexée, il peut être plus rapide d'utiliser EXISTS mais vous devrez tester votre situation spécifique.

La syntaxe JOIN est plus facile à lire et plus claire normalement également.

99
JNK
  • EXISTS est une semi-jointure
  • JOIN est une jointure

Donc, avec 3 lignes et 5 lignes correspondant

  • JOIN donne 15 lignes
  • EXISTS donne 3 lignes

Le résultat est l'effet de "court-circuit" mentionné par d'autres et pas besoin d'utiliser DISTINCT avec un JOIN. EXISTS est presque toujours plus rapide lorsque l'on recherche l'existence de lignes du côté n d'une relation 1: n.

38
gbn

EXISTS est principalement utilisé pour les raccourcis. Essentiellement, l'optimiseur se renfloue dès que la condition est remplie, il n'est donc pas nécessaire d'analyser la table entière (dans les versions modernes de SQL Server, cette optimisation peut également se produire pour IN(), bien que ce ne soit pas le cas toujours vrai). Ce comportement peut varier d'une requête à l'autre et, dans certains cas, la jointure peut en fait donner à l'optimiseur plus de possibilités pour effectuer son travail. Je pense donc qu'il est difficile de dire "c'est à ce moment-là que vous devriez utiliser EXISTS, et c'est à ce moment-là que vous ne devriez pas" parce que, comme beaucoup de choses, "cela dépend".

Cela dit, dans ce cas, étant donné que vous avez essentiellement une correspondance 1: 1 entre les tables, il est peu probable que vous constatiez une différence de performances et l'optimiseur produira probablement un plan similaire, voire identique. Vous pouvez voir quelque chose de différent si vous comparez la jointure/existe sur la table des ventes lorsque vous ajoutez 50000 lignes pour chaque titre (peu importe que vous devrez modifier votre requête de jointure pour supprimer les doublons, l'agrégat, qu'avez-vous).

10
Aaron Bertrand

Je trouve que c'est plus utile lorsque j'ai des lignes que j'aimerais exclure en fonction de leur interaction avec les autres lignes.

Par exemple,

SELECT * 
  FROM TABLE a
 WHERE a.val IN (1,2,3)
   AND NOT EXISTS(SELECT NULL
                    FROM TABLE b
                   WHERE b.id = a.id
                     AND b.val NOT IN (1, 2, 3))

Dans ce cas, j'exclus une ligne dans ma requête a basée sur un enregistrement b avec le même identifiant mais non valide.

Cela venait en fait d'un problème de production que j'avais au travail . La requête a déplacé la majeure partie de la logique d'exclusion dans la requête plutôt que dans l'application, faisant passer le temps de chargement de plus de 24 secondes à moins de 2 secondes. :-)

3
corsiKa