entity framework组采用最老的mySQL

我有一个庞大的物品清单,需要按一个属性对它们进行分组。 然后应选择每组中最老的一组。

简化示例:选择每个FirstName的最旧用户。

 using (ED.NWEntities ctx = new ED.NWEntities()) { IQueryable Result = ctx.User.GroupBy(x => x.FirstName) .Select(y => y.OrderBy(z => z.BirthDate) .FirstOrDefault()) .AsQueryable(); } 

class级User

 public partial class User { public int UserID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Nullable BirthDate { get; set; } } 

我想知道为什么这句话花了这么长时间,直到我在Result设置断点并查看生成的SQL语句:

 {SELECT `Apply1`.`UserID`, `Apply1`.`FIRSTNAME1` AS `FirstName`, `Apply1`.`LastName`, `Apply1`.`BirthDate` FROM (SELECT `Distinct1`.`FirstName`, (SELECT `Project2`.`UserID` FROM `User` AS `Project2` WHERE (`Distinct1`.`FirstName` = `Project2`.`FirstName`) OR ((`Distinct1`.`FirstName` IS NULL) AND (`Project2`.`FirstName` IS NULL)) ORDER BY `Project2`.`BirthDate` ASC LIMIT 1) AS `UserID`, (SELECT `Project2`.`FirstName` FROM `User` AS `Project2` WHERE (`Distinct1`.`FirstName` = `Project2`.`FirstName`) OR ((`Distinct1`.`FirstName` IS NULL) AND (`Project2`.`FirstName` IS NULL)) ORDER BY `Project2`.`BirthDate` ASC LIMIT 1) AS `FIRSTNAME1`, (SELECT `Project2`.`LastName` FROM `User` AS `Project2` WHERE (`Distinct1`.`FirstName` = `Project2`.`FirstName`) OR ((`Distinct1`.`FirstName` IS NULL) AND (`Project2`.`FirstName` IS NULL)) ORDER BY `Project2`.`BirthDate` ASC LIMIT 1) AS `LastName`, (SELECT `Project2`.`BirthDate` FROM `User` AS `Project2` WHERE (`Distinct1`.`FirstName` = `Project2`.`FirstName`) OR ((`Distinct1`.`FirstName` IS NULL) AND (`Project2`.`FirstName` IS NULL)) ORDER BY `Project2`.`BirthDate` ASC LIMIT 1) AS `BirthDate` FROM (SELECT DISTINCT `Extent1`.`FirstName` FROM `User` AS `Extent1`) AS `Distinct1`) AS `Apply1`} 

问题:有没有办法解决他的效率? 子选择很昂贵,EF每列产生一个。 我使用mySQL .NET Connector版本6.9.5.0

使用Jon Skeet对不同的答案 ..

 public static IEnumerable DistinctBy (this IEnumerable source, Func keySelector) { HashSet seenKeys = new HashSet(); foreach (TSource element in source) { if (seenKeys.Add(keySelector(element))) { yield return element; } } } 

你可以试试:

 using (ED.NWEntities ctx = new ED.NWEntities()) { IQueryable Result = ctx.User.OrderBy(y => y.BirthDate) .DistinctBy(z => z.FirstName) .AsQueryable(); } 

您可能会尝试做更多接近在sql中执行此操作的方法(没有“row_number like”函数)…并查看生成的内容。

 var maxAges = ctx.User.GroupBy(x => x.FirstName) .Select(g => new { firstName = g.Key, maxAge = g.Min(x => x.BirthDate) }); var result = from u in ctx.User join a in maxAges on new{f = u.FirstName, b =u.BirthDate} equals new{f = a.firstName, b =a.maxAge} select u; 

(混合流畅和查询语法,因为我发现连接的查询语法更清晰,但……这只是个人观点)

您首先对它们进行分组,然后对每个子查询进行排序。 当然,它会很慢。

尝试先订购表,这样你只需要这样做一次。 然后将它们分组并取第一个。

 IQueryable Result = ctx.User .OrderBy(x => x.BirthDate) .GroupBy(x => x.FirstName, (k,g) => g.FirstOrDefault()) .AsQueryable(); 

我很确定当你使用mySQL时,你可以创建一个与SELECT语句不同的GROUP BY子句。 换句话说,您选择的行不能是聚合函数的一部分。 所以这样的查询应该有效:

 SELECT FirstName ,LastName ,BirthDate FROM Users GROUP BY FirstName ORDER BY BirthDate 

请在mySQL查询浏览器中尝试此操作。 您可以直接使用此查询与您的entity framework上下文,如下所示:

 string query = ".."; // the query above var res = context.Database.SqlQuery(query).ToList(); 

看看这个,你之前和其他一些问题(比如这个 ),看起来像使用EF和MySQL一样痛苦。

您最终可以尝试此LINQ查询

 var query = db.User.Where(user => !db.User.Any( u => u.UserID != user.UserID && u.FirstName == user.FirstName && (u.BirthDate < user.BirthDate || (u.BirthDate == user.BirthDate && u.UserID < user.UserID)))); 

它生成这个简单的SQL查询

 SELECT `Extent1`.`UserID`, `Extent1`.`FirstName`, `Extent1`.`LastName`, `Extent1`.`BirthDate` FROM `Users` AS `Extent1` WHERE NOT EXISTS(SELECT 1 AS `C1` FROM `Users` AS `Extent2` WHERE ((`Extent2`.`UserID` != `Extent1`.`UserID`) AND (`Extent2`.`FirstName` = `Extent1`.`FirstName`)) AND ((`Extent2`.`BirthDate` < `Extent1`.`BirthDate`) OR ((`Extent2`.`BirthDate` = `Extent1`.`BirthDate`) AND (`Extent2`.`UserID` < `Extent1`.`UserID`)))) 

虽然我不确定会对性能产生什么影响。

您需要索引,这并不能保证最佳性能,因为EF生成的查询很可能是一个大型嵌套子查询。

如果性能仍然存在问题,则可以为每个组返回最旧的用户ID,并运行另一个查询以获取User对象。

更糟糕的情况是,使用内联sql,视图或存储过程。

因为我不使用Mysql,而且我没有你的索引,我将为你留下这个任务。

  var oldestUsers = (from u in users group u by u.FirstName into grp select new { grp.Key, oldestUser = (from u in grp orderby u.BirthDate descending select u).First() }).ToList(); foreach (var u in oldestUsers) { Console.WriteLine("{0} {1:D}", u.oldestUser.FirstName, u.oldestUser.BirthDate); }