2016-12-05 83 views
1

我想選擇一個用戶在有序列表中的位置。實體框架選擇項目位置在有序列表中

我只能通過將所有項目加載到內存中,然後使用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;   
    } 

我怎麼能做到這一點更有效?

回答

0

使用Skip:要考慮元素501 ..

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

這不會幫助你找到元素的位置。這隻會幫助你,如果你已經知道你想要的元素的位置。 – gnud

+0

@gnud你說得對,我理解了這個問題。 –

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

answer by gnud提出了正確的想法,但不幸的是,兩個提供的解決方案都不能很好地轉化爲SQL。這是因爲EF6查詢翻譯對您編寫LINQ查詢的方式仍然很敏感(不幸的是)。

這裏是產生一個好的翻譯的實現(通過實驗發現)(改decimal?投給可空類型的Points屬性類型 - 需要投以避免NRE的情況下設置爲空):

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; 
} 

其產生2個SQLS這樣的:

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] 

SELECT 
    [GroupBy2].[A1] AS [C1] 
    FROM (SELECT 
     COUNT(1) AS [A1] 
     FROM (SELECT 
      [Extent1].[UserId] AS [K1], 
      SUM([Extent2].[Points]) AS [A1] 
      FROM [dbo].[UserIqAnswers] AS [Extent1] 
      INNER JOIN [dbo].[IqQuestions] AS [Extent2] ON ([Extent1].[IqQuestion_Id] = [Extent2].[Id]) AND ([Extent2].[CorrectAnswer] = [Extent1].[Answer]) 
      WHERE [Extent1].[UserId] <> @p__linq__0 
      GROUP BY [Extent1].[UserId] 
     ) AS [GroupBy1] 
     WHERE ([GroupBy1].[A1] < @p__linq__1) OR (([GroupBy1].[A1] = @p__linq__2) AND ([GroupBy1].[K1] < @p__linq__3)) 
    ) AS [GroupBy2]