重写WHERE NOT IN子句的最佳方法是什么?

时间:2010-01-05 19:12:12

标签: sql sql-server sql-server-2005

我有一个类似下面的查询:

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')

由于“NOT IN”条款,这非常慢。我尝试使用左外连接重写它,使它看起来像:

   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')

哪个更快但结果不匹配。我这次重写有什么问题吗?有更好的方法吗?

4 个答案:

答案 0 :(得分:2)

也许这是一个错字,但在第一个查询中,您正在比较 StuKey 列,而在第二个查询中,您正在加入

在性能方面,我希望这两个查询会产生一个非常相似的(如果不是相同的)执行计划。你应该检查两者的计划。

另外,请确保在运行之间清除数据缓存,因为它们实际上可以执行相同的操作,但由于缓存数据,第二个似乎更快。

答案 1 :(得分:1)

我认为问题在于:

ser.StuKey not in (select StuKey

与:相比:

ON ser.key = ser2.key

所以重写应该是:

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

(注意我也删除了ser2.EnrlmtStatCode ='4321'并将其重写为列比较)

答案 2 :(得分:1)

试试这个

  • NOT EXISTS
  • 也许无需关联ser2.StartDate > ser.wd,因为ser.wd >= @FromDate

此外:

  • 如果必须使用ser2.StartDate > ser.wd,它们的数据类型是否相同?
  • 所有数据类型都排成一行吗?例如:@ FrromDate,@ ThdDate,StartDate,wd等?

因此:

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)

答案 3 :(得分:0)

这将是效率与清晰度之间的良好平衡:

    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

您可以通过将子查询设为UDF来提高速度。对于子查询执行大量工作的大量记录,请考虑将所有内容放在UDF或过程中,并使用子查询的结果填充临时表,在主查询中使用该表,然后通过擦除进行清理临时表。