实体框架在有序列表中选择项目位置

时间:2016-12-05 21:05:31

标签: c# sql-server entity-framework-6 linq-to-entities

我想在有序列表中选择用户的位置。

我只能通过将所有项目加载到内存中然后使用IndexAt()方法来查找位置来使其工作。但是,当数据库中有许多行时,这种方法效果不佳。

        public static async Task<int> GetUsersRank(DbEntities db, string userId)
    {           
      var items = await db.UserIqAnswers.Where(x => x.IqQuestion.CorrectAnswer == x.Answer).GroupBy(x => x.UserId).Select(x => new { userId = x.Key, points = x.Sum(y => y.IqQuestion.Points) })
         .OrderBy(x => x.points)
         .ToListAsync();
      return items.FindIndex(x => x.userId == userId) + 1;         
    }

我怎样才能更有效地做到这一点?

3 个答案:

答案 0 :(得分:3)

这样的东西? 这将运行两个查询。一个用于加载当前用户的信息,另一个用于加载列表中的位置。 但是,查询应该相当有效。第二个应该在SQL服务器端以COUNT()完成。

var sumByUser = db.UserIqAnswers.Where(x => x.IqQuestion.CorrectAnswer == x.Answer)
    .GroupBy(x => x.UserId)
    .Select(x => new { userId = x.Key, points = x.Sum(y => y.IqQuestion.Points) });
var currentUser = sumByUser.Where(x => x.userId == userId).Single();
var rank = sumByUser.Where(x => x.points > currentUser.points).Count();

请注意,'sumByUser'查询永远不会被执行,它只是用作接下来两个查询的基础。

您可以使用LINQ语法将其重写为一个查询:

(from currentUser in sumByUser.Where(x => x.userId == userId)
 from rank in sumByUser.Where(x => x.points > currentUser.points
 select new { currentUser, rank = rank.Count()}).Single()

但我会检查生成的SQL以确定。

答案 1 :(得分:1)

answer by gnud提出了正确的想法,但不幸的是两个提供的解决方案都没有很好地转换为SQL。这是因为EF6查询转换仍然对编写LINQ查询的方式很敏感(不幸的是)。

这是一个实现(通过实验找到)产生良好的翻译(将var doc = richTextBox.Document; foreach(var c in evt.Changes.Where(x => x.AddedLength > 1)) { var change = new TextRange( doc.ContentStart.GetPositionAtOffset(c.Offset), doc.ContentStart.GetPositionAtOffset(c.Offset + c.AddedLength)); Debug.WriteLine($"Change: <{change.Text}>"); } 强制转换为decimal?属性类型的可空类型 - 需要强制转换以避免NRE,以防万一set为空):

Points

生成2个这样的SQL:

public static async Task<int> GetUsersRank(DbEntities db, string userId)
{
    var userPoints = await db.UserIqAnswers
        .Where(x => x.UserId == userId && x.IqQuestion.CorrectAnswer == x.Answer)
        .SumAsync(x => (decimal?)x.IqQuestion.Points) ?? 0;

    var rank = await db.UserIqAnswers
        .Select(x => new { x.UserId, x.Answer, x.IqQuestion })
        .Where(x => x.UserId != userId && x.IqQuestion.CorrectAnswer == x.Answer)
        .GroupBy(x => x.UserId)
        .Select(x => new { userId = x.Key, points = x.Sum(y => y.IqQuestion.Points) })
        .CountAsync(x => x.points < userPoints || (x.points == userPoints && string.Compare(x.userId, userId) < 0));

    return rank;
}

SELECT
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT
        SUM([Extent2].[Points]) AS [A1]
        FROM  [dbo].[UserIqAnswers] AS [Extent1]
        INNER JOIN [dbo].[IqQuestions] AS [Extent2] ON ([Extent1].[Answer] = [Extent2].[CorrectAnswer]) AND ([Extent1].[IqQuestion_Id] = [Extent2].[Id])
        WHERE [Extent1].[UserId] = @p__linq__0
    )  AS [GroupBy1]

答案 2 :(得分:0)

使用 Skip :获取元素501 ..

queryable.Skip(500).Take(1).ToList();