2010-01-05 6 views
3

J'ai une requête comme celle ci-dessous:La meilleure façon de réécrire une clause WHERE NOT IN?

Select ser.key 
From dbo.Enrlmt ser  
Where ser.wd >= @FromDate AND ser.wd <= @ThrouDate AND 
     ser.EnrlmtStatCode = '4321' AND 
     ser.StuExitCatgCode in ('1','2','3','4','5','6','7') AND 
     ser.Key not in (select Key 
          from Enrlmt ser2 
          where ser2.StartDate <= @AsOfDate 
          AND ser2.StartDate > ser.wd 
          AND ser2.EnrlmtStatCode = '4321') 

extrêmement lent en raison de la clause « NOT IN ». J'ai essayé de le réécrire en utilisant une jointure externe gauche pour qu'elle ressemblait à:

Select ser.key 
    From dbo.Enrlmt ser  
LEFT JOIN dbo.Enrlmt ser2 ON ser.key = ser2.key 
         AND ser2.StartDate <= @AsOfDate 
         AND ser2.StartDate > ser.wd 
         AND ser2.EnrlmtStatCode = '4321' 
    Where ser2.key is null 
    AND ser.wd >= @FromDate 
    AND ser.wd <= @ThrouDate 
    AND ser.EnrlmtStatCode = '4321' 
    AND ser.StuExitCatgCode in ('1','2','3','4','5','6','7') 

Ce qui est beaucoup plus rapide, mais les résultats ne correspondent pas. Est-ce que je fais quelque chose de mal avec cette réécriture? Y a-t-il une meilleure manière de faire cela?

+2

'NOT IN' est pas lent - c'est parce que vous utilisez un sous-requête corrélée. –

+5

L'entrée NOT IN * n'entraîne pas votre problème de performance, en supposant que les colonnes ne soient pas Nullable: http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join- is-null-sql-server/ –

+1

La recherche de nombres comme des chaînes me fait aussi grincer des dents –

Répondre

2

Peut-être est une faute de frappe, mais dans la première requête, vous comparez la colonne Stukey alors que dans la deuxième requête que vous joindrez sur clés

Côté performance, je vous attendriez les 2 requêtes pour aboutir à un plan d'exécution très similaire, sinon le même. Vous devriez vérifier les plans des deux à voir.

De même, assurez-vous que vous effacez le cache de données entre les exécutions, car ils pourraient effectivement effectuer la même chose, mais en raison de la mise en cache des données, le 2e semble être plus rapide.

+0

Oui. Je pense que c'est ça. –

+0

le StuKey vs Key était une faute de frappe. Merci de m'avoir signalé. Je pensais que l'utilisation d'une clause IN faisait que les index n'étaient pas utilisés correctement, c'est pourquoi je l'ai déplacé vers une jointure. Suis-je hors de la base? –

1

Je pense que le problème est ici:

ser.StuKey not in (select StuKey 

par rapport à:

ON ser.key = ser2.key 

Ainsi, la réécriture doit être:

SELECT  ser.key 
FROM  dbo.Enrlmt ser 
LEFT JOIN Enrlmt  ser2 
ON   ser.StuKey   = ser2.Stukey 
AND   ser.EnrlmtStatCode = ser2.EnrlmtStatCode 
AND   ser2.StartDate  > ser.wd 
AND   ser2.StartDate  <= @AsOfDate 
WHERE  ser.wd    >= @FromDate 
AND   ser.wd    <= @ThrouDate 
AND   ser.EnrlmtStatCode = '4321' 
AND   ser.StuExitCatgCode in ('1','2','3','4','5','6','7') 
AND   ser2.key IS NULL 

(note I ucut également la SER2 .EnrlmtStatCode = '4321' et l'a réécrit pour une comparaison de colonnes)

+0

StuKey était une faute de frappe, j'ai mis à jour la requête originale. –

0

Ce sera un bon équilibre entre l'efficacité et la clarté:

Select ser.key 
From 
    dbo.Enrlmt ser 
    Left Join (select StuKey 
      from Enrlmt 
      where Enrlmt.StartDate <= @AsOfDate AND 
      Enrlmt.EnrlmtStatCode = '4321') As ser2 
    ON ser.key = ser2.key And ser2.StartDate > ser.wd 
Where 
     ser.wd >= @FromDate AND ser.wd <= @ThrouDate AND 
     ser.EnrlmtStatCode = '4321' AND 
     ser.StuExitCatgCode in ('1','2','3','4','5','6','7') AND 
     ser2.key Is Null 

Vous pourriez eek plus de vitesse en faisant la sous-requête UDF. Pour les grandes quantités d'enregistrements pour lesquels la sous-requête fait beaucoup de travail, envisagez de placer tout cela dans une UDF ou une procédure et de remplir une table temporaire avec les résultats de la sous-requête, en utilisant cette table dans la requête principale. la table temporaire.

1

Essayez cette

  • PAS EXISTE
  • Peut-être pas besoin de mettre en corrélation ser2.StartDate > ser.wd parce ser.wd >= @FromDate?

aussi:

  • Si vous devez utiliser ser2.StartDate > ser.wd, sont-ils le même type de données?
  • Tous les types de données s'alignent-ils? Par exemple: @FromDate, @ThrouDate, StartDate, wd etc?

ainsi:

Select 
    ser.key 
From 
    dbo.Enrlmt ser  
Where 
    ser.wd >= @FromDate AND ser.wd <= @ThrouDate AND 
    ser.EnrlmtStatCode = '4321' AND 
    ser.StuExitCatgCode in ('1','2','3','4','5','6','7') AND 
    not EXISTS (select * 
          from Enrlmt ser2 
          where ser2.StartDate <= @AsOfDate 
          AND ser2.EnrlmtStatCode = '4321' 
          AND ser2.StartDate > @FromDate --ser.wd?? 
          AND ser2.Key = ser.Key) 
+0

Cette réponse a fonctionné pour moi dans une base de données H2. 'SÉLECTIONNEZ C1 À PARTIR DE T1 O C1 C1 NE S'INSCRIT PAS (SÉLECTIONNEZ C1 À PARTIR DE T2);' n'est pas revenu au moment où je l'ai annulé. 'SELECT C1 FROM T1 O NOT N'EXISTE PAS (SELECT C1 FROM T2);' revient presque instantanément. –