簡體   English   中英

在多列上有效使用row_number

[英]Efficient using of row_number over multiple columns

我正在嘗試在SQL Server 2008 R2中實現有效的用戶評級,其中記錄在不斷變化,每次寫入用戶數據都會導致隨后的評級讀取,而該讀取只是多個列上的ROW_NUMBER

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

每次寫入Scores表都會導致隨后的讀取,如下所示:

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

也有查詢使用相同的ROW_NUMBER對評級頁面進行查詢。 當前,表Scores包含約3萬行,當我運行后一個查詢時,執行計划看起來不錯,但執行時間約為100-200ms! 在高峰工作負載期間每秒進行幾次用戶評級更新是不可接受的。

我想知道是否有更有效的方法來組織用戶評分?

更新1:感謝Gordon Linoff,我進行了進一步的實驗,獲得用戶評分的最終優化方法是使用上面的查詢和以下修改的索引( 非唯一! ):

CREATE NONCLUSTERED INDEX IX_Scores_Rating ON dbo.Scores
(
    MaxLevel desc, BestDiff desc, BestDiffGames desc, 
    BestDiffLastDate desc, MaxLevelLastWinDate desc,
    UserId
)
GO

更新2:多虧了Mikael Eriksson ,下面的top 1查詢甚至使中級用戶的查詢速度也提高了兩倍 評分最高的用戶查詢速度提高了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()而不是row_number() ,因此它對領帶的處理方式不同。 如果您可以將這些列放入帶有索引的單個列中,則應該很快。

您可以對多個列執行相同的操作,但是邏輯會變得復雜,而且我不確定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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM