Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 在多列上高效使用行数_Sql_Sql Server_Sql Server 2008_Tsql_Sql Order By - Fatal编程技术网

Sql 在多列上高效使用行数

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,

我正在尝试在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,
    [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)