Sql 在多列上高效使用行数
我正在尝试在SQL Server 2008 R2中实现高效的用户评级,在SQL Server 2008 R2中,记录不断变化,每次写入用户数据都会导致后续的评级读取,而这只是多列上的一个Sql 在多列上高效使用行数,sql,sql-server,sql-server-2008,tsql,sql-order-by,Sql,Sql Server,Sql Server 2008,Tsql,Sql Order By,我正在尝试在SQL Server 2008 R2中实现高效的用户评级,在SQL Server 2008 R2中,记录不断变化,每次写入用户数据都会导致后续的评级读取,而这只是多列上的一个行数 CREATE TABLE [dbo].[Scores] ( [Id] int NOT NULL IDENTITY (1, 1), [UserId] int NOT NULL, [MaxLevel] int NOT NULL, [BestDiff] int NOT NULL,
行数
CREATE TABLE [dbo].[Scores]
(
[Id] int NOT NULL IDENTITY (1, 1),
[UserId] int NOT NULL,
[MaxLevel] int NOT NULL,
[BestDiff] int NOT NULL,
[BestDiffGames] int NOT NULL,
[BestDiffLastDate] datetime NOT NULL,
[MaxLevelLastWinDate] datetime,
-- other statistics
CONSTRAINT [PK_Scores] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Scores_REF_Users] FOREIGN KEY([UserId]) REFERENCES [dbo].[Users] ([Id])
)
GO
CREATE UNIQUE NONCLUSTERED INDEX IX_Scores_User ON dbo.Scores
(
UserId
)
GO
CREATE NONCLUSTERED INDEX IX_Scores_Rating ON dbo.Scores
(
MaxLevel desc, BestDiff desc, BestDiffGames desc,
BestDiffLastDate desc, MaxLevelLastWinDate desc
)
GO
每次写入分数表
都会导致后续读取,如下所示:
with Ratings (Rating, UserId) as
(
select (ROW_NUMBER() over
(order by MaxLevel desc, BestDiff desc, BestDiffGames desc,
BestDiffLastDate desc, MaxLevelLastWinDate desc)) as Rating,
UserId
from Scores with (nolock)
)
select @Rating = Rating
from Ratings
where UserId = @UserId
此外,还可以使用相同的行号查询评级页面。
当前表Scores
包含大约30K行,当我运行后一个查询时,执行计划看起来不错,但它的执行持续时间约为100-200ms!在峰值工作负载期间,每秒几次用户评级更新是不可接受的
我想知道是否有更有效的方法来组织用户评级
更新1:多亏了Gordon Linoff我做了进一步的实验,获得用户评级的最终优化方法是使用上面的查询和下面修改的索引(非唯一!):
更新2:多亏了Mikael Eriksson以下带有top 1
的查询将查询速度提高了2x,即使对于中等级别的用户也是如此!排名靠前的用户获得了高达8倍的查询速度。这些速度提升数字是在优化1(索引更改)后实现的,因此当前执行时间从最初的100-200ms下降到2-16ms,比最初快6-100倍
with Ratings (Rating, UserId) as
(
select (ROW_NUMBER() over
(order by MaxLevel desc, BestDiff desc, BestDiffGames desc,
BestDiffLastDate desc, MaxLevelLastWinDate desc)) as Rating,
UserId
from Scores with (nolock)
)
select top 1 @Rating = Rating
from Ratings
where UserId = @UserId
100-200毫秒似乎没那么糟糕
如果您只有一列评级,那么您可能可以:
select @Rating = 1 + count(*)
from scores s cross join
(select * from scores s where userId = @UserId) su
where s.score > su.score;
如果你有领带,这就不完全一样了;它相当于rank()
而不是行号()
,因此它处理领带的方式不同。如果可以将这些列合并到一个带有索引的列中,那么这应该很快
您可以对多个列执行相同的操作,但逻辑会变得复杂,而且我不能100%确定索引是否总是得到正确使用。比如:
where s.score > su.score or
(s.score = su.score and s.bestdiff > su.bestdif) or
(s.score = su.score and s.bestdiff = su.bestdif and s.BestDiffGames > su.BestDiffGames) or
(s.score = su.score and s.bestdiff = su.bestdif and s.BestDiffGames = su.BestDiffGames and s.MaxLevelLastWinDate > su.MaxLevelLastWinDate)
当您有多个具有相同用户ID的行时,返回什么评级?对我来说,它看起来可以是指定用户的任何评级值。您可能应该在主查询中添加一个min()或max()。或者在UserId上添加一个唯一的约束?@MikaelEriksson,很抱歉没有提到它:是的,UserId上有一个唯一的索引。我将这个答案标记为正确的,因为使用提供的索引,您处理多个或语句的速度比使用行数的速度快5-10倍。然而,当我将列UserId
添加到IX_Scores_Rating
的末尾时,速度正好相反:使用ROW_NUMBER
的查询比使用或语句的查询快1.4倍,而后者的性能不受索引更改的影响。我使用SQL Management Studio中的客户端统计数据来衡量查询数据库服务器上的时间。索引调整后使用ROW\u NUMBER
查询比使用或语句查询快的原因很可能是数据库引擎必须执行的SELECT语句的数量:客户端统计报告两个查询的SELECT语句数量分别为1和3。@IPSUS您还可以尝试添加top(1)
到行号版本。这可能会对级别较低的用户产生影响。当找到一行时,SQL server将停止枚举,因为UserId是唯一的,所以结果不会有任何差别。@Mikael,这令人印象深刻-top 1
将查询速度提高了2倍,即使对于中等级别的用户也是如此!顶级用户的查询速度提高了8倍。
where s.score > su.score or
(s.score = su.score and s.bestdiff > su.bestdif) or
(s.score = su.score and s.bestdiff = su.bestdif and s.BestDiffGames > su.BestDiffGames) or
(s.score = su.score and s.bestdiff = su.bestdif and s.BestDiffGames = su.BestDiffGames and s.MaxLevelLastWinDate > su.MaxLevelLastWinDate)