实体框架组采用最老的mySQL

时间:2016-04-22 06:26:18

标签: c# mysql entity-framework linq

我有一个庞大的物品清单,需要按一个物业分组。然后应该选择每组中最老的。

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

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

班级User

public partial class User
{
    public int UserID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Nullable<System.DateTime> 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

6 个答案:

答案 0 :(得分:3)

在不同的情况下使用Jon Skeet的answer

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

您可以尝试:

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

答案 1 :(得分:2)

你可能会尝试做一些更接近你在sql中做的事情(没有“row_number like”函数)...并看看生成了什么。

 app:tabGravity="fill"
 app:tabMode="fixed"

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

答案 2 :(得分:1)

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

首先尝试订购表格,这样您只需要执行一次。然后将它们分组并取第一个。

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

答案 3 :(得分:1)

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

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

请在mySQL查询浏览器中尝试此操作。您可以直接使用此查询与您的实体框架上下文:

string query = ".."; // the query above

var res = context.Database.SqlQuery<Users>(query).ToList();

答案 4 :(得分:0)

看看这个,你的previous和其他一些问题(比如this),看起来像使用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`))))

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

答案 5 :(得分:0)

您需要索引,但这并不能保证最佳性能,因为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);
    }