【问题标题】:Performance with NOT EXISTS - t-sql query不存在的性能 - t-sql 查询
【发布时间】:2013-02-22 21:49:44
【问题描述】:

这个(为简单起见进行了修改)查询是更大查询的一部分,并与其他选择在日期上连接。但是,我已将此部分固定为狗慢。假设我有一个 UserLoginHistory-table 记录用户的每次登录。对于每个用户,我想要他们首次登录的日期。(稍后在查询中,我按 LogDate 分组以获取每天有多少首次登录。)

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

显然不存在部分是慢的。但我不知道如何用更有效的方法来代替它。

使用较小的 UserLogHistory-count,性能没有问题。当我达到 15 000 左右时,它开始变慢。也许我应该将每天的结果批处理到另一个表中,但我想为这个查询找到一个更好的解决方案,因为那里应该有一个......

感谢您的宝贵时间!

【问题讨论】:

  • 根据定义,NOT EXISTS 必须执行表扫描。调整其性能的关键是使必须扫描的关系尽可能小,最好是非聚集索引。在不知道您的表上存在哪些索引的情况下,无法提供更具体的建议。
  • 在大多数情况下,这是not in 和left outer join where key is null 之间的最佳方法:Aaron Bertrand has tested it。
  • 性能问题往往与平台相关,您使用的是哪个数据库:SQL Server 还是 Sybase?你有什么索引? 15,000 行并不是很多,因此您的索引可能不是最佳的。

标签: sql performance tsql group-by not-exists


【解决方案1】:

您可以使用行编号方法:

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 的唯一组合。

【讨论】:

  • 现在有了天壤之别。
【解决方案2】:

如果您只对这两个字段感兴趣,您能不使用简单的聚合吗?

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

【讨论】:

  • 谢谢,这可能适用于一个简单的场景,但我选择了 dan1111 的答案,因为它对我来说更灵活。
猜你喜欢
  • 2015-05-27
  • 1970-01-01
  • 2011-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多