2013-03-07 43 views
3

这(为简单起见修改)查询是较大查询的一部分,并与其他选择一起加入日期。不过,我已将此部分固定在狗身上。假设我有一个UserLoginHistory表,记录用户的每个登录信息。对于每一个用户我想他们第一次登录的日期。(后来在查询中,我组由LOGDATE得到多少第一次登录有每一天都是。)不存在的性能 - t-sql查询

select 
    LogDate, --(this value is only date, no time) 
    UserId 
from 
    UserLoginHistory ul 
where 
    not exists 
     (
      select 
       * 
      from 
       UserLoginHistory ulPrevious 
      where 
       ulPrevious.LogDate < ul.LogDate 
       and ul.UserId = ulPrevious.UserId 
     ) 
group by ul.LogDate, ul.UserId 

显然NOT EXISTS部分是慢的。但我无法弄清楚如何用更有效的方式替代它来完成同样的工作。

对于小UserLogHistory计数,性能没有问题。当我达到约15 000时,它开始变慢。也许我应该将每天的结果批量放入另一个表格,但是我希望找到更好的解决方案,因为应该有一个更好的解决方案...

感谢您的时间!

+0

根据定义,NOT EXISTS必须执行表扫描。调整其性能的关键是让必须扫描的关系尽可能小,理想情况下为非聚集索引。如果不知道桌面上有什么指数,就不可能提供更具体的建议。 – 2013-03-07 15:52:03

+3

在大多数情况下,这是'not in'和'left outer join之间'key null'之间最好的方法:[Aaron Bertrand测试了它](http://www.sqlperformance.com/2012/12/t-sql -queries /左防半联接)。 – 2013-03-07 15:53:34

+2

性能问题往往是平台特定的,您使用的数据库是:SQL Server还是Sybase?你有什么指标在桌子上? 15,000行不是很多,所以您的索引似乎可能不是最优的。 – Pondlife 2013-03-07 16:56:20

回答

4

您可以使用一排编号方法:

select LogDate,UserId from (
    select 
     LogDate, 
     UserId 
     row_number() over (partition by UserId order by LogDate) as rown 
    from 
     UserLoginHistory ul 
) 
where rown = 1 

行每个ID被LOGDATE编号,所以最早的一个将永远编号为1

注:我不认为原始查询中的group by是必要的 - not exists子句应保证您只能获得UserId和LogDate的唯一组合。

+0

现在,做了一个世界的差异。 – cederlof 2013-03-08 08:00:05

4

如果这些是您感兴趣的唯一两个字段,您能否使用简单的聚合?

SELECT LogDate = MIN(LogDate), 
     UserID 
FROM UserLoginHistory 
GROUP BY UserID; 
+0

谢谢,这可能适用于一个简单的场景,但我选择了dan1111的答案,因为它对我来说会更灵活。 – cederlof 2013-03-08 08:02:02