我的SQL性能指南有一个高级SQL问题: - )
我目前正在尝试了解更大的应用程序中的某些行为,但它归结为针对这两个表的查询:
Users
表 - 大约750个条目,UserId
(varchar(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%或更多是指不存在的用户(即这些行有UserId
在Users
表中不存在。
现在突然,查询变得非常慢(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
条目时看到良好的性能现有用户 - 但似乎并非如此。
为什么不?
有什么想法吗?思考?要尝试的事情??
答案 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;
然后考虑获取其他专栏。