我正在使用此代码对游戏中的玩家进行排名。
private void RecalculateUserRanks(GWDatabase db)
{
// RankedScore is a precalculated Double.
var users = db.UserStatistics.Where(x => x.RankedScore > 0);
var usersWithRank = users.OrderByDescending(x => x.RankedScore)
.Select(x => new
{
x.Id,
x.RankedScore
});
int position = 0;
foreach (var u in usersWithRank)
{
position++;
db.UserStatistics.First(x => x.Id == u.Id).Rank = position;
}
db.SaveChanges();
}
这不是最漂亮的,随着玩家数量的增加,这可能需要一些时间并使用一些记忆。
我可以在纯TSQL中这样做:
;WITH r AS
(
SELECT
[Id]
,[RankedScore]
,ROW_NUMBER() OVER(ORDER BY [RankedScore] DESC) AS Rnk
FROM [dbo].[UsersStatistics]
)
UPDATE u
SET u.Rank = r.Rnk
FROM [dbo].[UsersStatistics] u
INNER JOIN r ON r.Id = u.Id
但我宁愿将所有逻辑保留在C#代码中,因为数据库现在一直在重建(所有其他逻辑也在那里)。
所以我的问题是,如果有一种更聪明的方法可以在C#LINQ(或Lambda,如果那是你的东西)中执行此操作而不在for循环中迭代它,而不将所有数据拖到SQL之外?
答案 0 :(得分:1)
我假设有效率'你的意思是“有效阅读”。为了加快计算速度,您可以考虑使用log n
的排序列表;那些使用//get sorted list of IDs
var SortedIds = db.UserStatistics
.OrderByDescending(x => x.RankedScore)
.Select(x => x.Id);
//Fill in Values into result-set
db.UserStatistics = db.UserStatistics
.Where(x => x.RankedScore > 0)
.ForEach(x => u.Rank = SortedIds.IndexOf(x.id));
时间插入新成员时保持自己排序。
这与你发布的几乎相同,除了懒惰评估可能会节省一点时间:
db.UserStatistics = db.UserStatistics.ForEach(u =>
u.Rank = db.UserStatistics
.Where(x => x.RankedScore > 0)
.OrderByDescending(x => x.RankedScore)
.IndexOf(u.id));
将排名和未排名的球员放在一起似乎有些不一致。
这样可以让未排名的玩家获得排名-1,同时保存一步。缺点是,所有用户都将被更改,而不是那些具有等级的用户:
self.mainView