SQL Server查询 - 没有按预期执行,不像我想象的那样

时间:2014-11-11 20:06:55

标签: sql-server performance query-performance

我的SQL性能指南有一个高级SQL问题: - )

我目前正在尝试了解更大的应用程序中的某些行为,但它归结为针对这两个表的查询:

  • Users表 - 大约750个条目,UserIdvarchar(50))作为群集PK
  • ActionLog表 - 数百万条目,包括UserId - 但没有FK关系

对于我的ASP.NET应用程序中的网格,我试图让所有用户加上他们上次日志条目的日期。

当前使用的SQL语句如下所示:

SELECT
     UserId, (other columns),
     LastLogDate = (SELECT TOP (1) [Timestamp] FROM dbo.ActionLog a WHERE a.UserId = u.UserId ORDER BY [Timestamp] DESC) 
FROM
     dbo.Users u;

并返回要显示的行 - 但它相当慢(大约20秒)。

我的第一个想法是在ActionLog上的UserId表格上添加一个索引,并在其中加入Timestamp列:

CREATE NONCLUSTERED INDEX [IDX_UserId]
ON [dbo].[ActionLog]([UserId] ASC)
INCLUDE ([Timestamp])

行现在很快返回 - 在2秒内,ActionLog表中有350'000个条目,我的索引正在使用,正如执行计划显示的那样。一切似乎都很好。

现在,为了近似生产场景,我们在ActionLog表中加载了大约200万行,其中95%或更多是指不存在的用户(即这些行有UserIdUsers表中不存在。

现在突然,查询变得非常慢(24分钟!),并且索引不再被使用。

我认为由于ActionLog表中的绝大多数条目都不与现有用户对齐,如果我使用过滤索引,我会看到性能提升 - 在没有相应用户的情况下“清除”所有那些杂乱的条目 - 所以我创建了这个索引(替换之前存在的另一个):

CREATE NONCLUSTERED INDEX [IDX_UserId]
ON [dbo].[Log]([UserId] ASC)
INCLUDE ([Timestamp])
WHERE UserId <> 'user'    -- that's the fixed, non-existing "UserId" I wanted to avoid

但令我沮丧的是 - 查询仍然大致相同 - 需要20多分钟才能完成。我更新了统计数据 - 没有变化 - 仍然非常慢。

有趣的事(对我来说)是:当我删除索引并重新创建它时 - &gt;现在查询真的很快(再次不到3秒)。 WOW!

但是当我再次开始添加更多条目时,查询“倾斜”并变得非常慢......

我不完全理解为什么会发生这种情况 - 我认为通过过滤索引消除所有这些“流氓”条目,我会在尝试找到最新的ActionLog条目时看到良好的性能现有用户 - 但似乎并非如此。

为什么不?

有什么想法吗?思考?要尝试的事情??

3 个答案:

答案 0 :(得分:3)

首先,INCLUDE这里不是最好的选择。您按输入日期排序,但包含的列未排序。更好的解决方案是:

CREATE NONCLUSTERED INDEX [IX_ActionLog_UserIdTimestamp] ON [dbo].[ActionLog]
([UserId], [Timestamp]);

其次,看起来您可能需要比自动更新更频繁地更新索引的统计信息。我曾经看过一些情况,在类似于你的情况下,由于过多的插入,我不得不每10分钟更新一次统计数据。那可是2005年。

答案 1 :(得分:2)

尝试此查询,看看它如何与原始索引或@Roger Wolf建议修改后的效果:

SELECT u.UserId, a.LastLogDate 
FROM dbo.Users u
INNER JOIN (
    SELECT UserId, Max([TimeStamp]) AS LastLogDate
    FROM dbo.ActionLog 
    WHERE userid <> 'user' -- the user to filter out
    GROUP BY UserId
) a ON a.UserId = u.UserId

如果它很糟糕我会删除答案:)

答案 2 :(得分:-1)

删除子选择:

SELECT u.UserId, Max(a.TimeStamp) As LastLogDate
FROM   dbo.Users u
,      dob.ActionLog a
Where  a.UserId = u.UserId
Group By u.UserId;

然后考虑获取其他专栏。