C# LINQ查询(MYSQL EF)-不同和最新记录

5
我是一名有用的助手,可以为您翻译以下内容:

我有一个表,让我们称之为“记录”。包含: ID(整数)| CustID(整数)| 时间(日期时间)| 数据(字符串)

我需要每个客户的最新(最近)记录:

SQL

select * from record as i group by i.custid having max(id);

LINQ版本1

dgvLatestDistinctRec.DataSource = from g in ee.Records
                                  group g by g.CustID into grp
                                  select grp.LastOrDefault();

这会抛出一个错误:

System.NotSupportedException由用户代码处理。消息 = LINQ to Entities不认识方法 'Faizan_Kazi_Utils.Record LastOrDefault [Record](System.Collections.Generic.IEnumerable'1 [Faizan_Kazi_Utils.Record])' 方法,且此方法无法被转换为存储表达式。源 = System.Data.Entity

LINQ 版本 2

var list = (from g in ee.Records
            group g by g.CustID into grp
            select grp).ToList();
Record[] list2 = (from grp in list
                  select grp.LastOrDefault()).ToArray();
dgvLatestDistinctRec.DataSource = list2;

这样做虽然可行,但效率低下,因为它会将数据库中的所有记录加载到内存中,然后仅提取每个组中最后一个(最近的成员)。
是否有任何LINQ解决方案可以接近所提到的SQL解决方案的效率和可读性?

你是如何实现Mysql+LINQ的?DBSet是否支持Mysql?如果支持,能否告诉我如何实现?我一直想实现这个,但上周终于放弃了。 - prabhakaran
我只需要核心要点,至少需要你放置连接字符串的位置。 - prabhakaran
5个回答

4

更新:

var results = (from rec in Record group rec by rec.CustID into grp 
    select new
    {
        CustID = grp.Key,
        ID = grp.OrderByDescending(r => r.ID).Select(x => x.ID).FirstOrDefault(),
        Data = grp.OrderByDescending(r => r.ID).Select(x => x.Data).FirstOrDefault()
    }
);

我创建了一个测试表并编写了一个Linq->SQL查询,它将完全符合您的需求。看一下这个,让我知道你的想法。需要记住的唯一一件事是,如果此查询被扩展,我相信它将在select new之后为每个CustID记录运行一次查询到数据库中。确认的唯一方法是在运行查询时运行SQL跟踪器,有关信息请访问此处:http://www.foliotek.com/devblog/tuning-sql-server-for-programmers/
原始代码如下:

你能不能做类似这样的事情?from g in ee.Records where g.CustID == (from x in ee.Records where (g.CustID == x.CustID) && (g.ID == x.Max(ID)).Select(r => r.CustID))

这只是伪代码,但希望你明白我的意思。


好的,我尝试了你所说的内容,但是在两个where语句中出现错误:Delegate System.Func<Faizan_Kazi_Utils.Record,int,bool>'不接受1个参数。 - Faizan Kazi
在Max(ID)中使用ID时出现以下错误:当前上下文中不存在'id'的名称。 - Faizan Kazi
我明白你想做什么,但我一直无法进行必要的更改以使其正常工作 :s - Faizan Kazi
我选择使用OrderByDescending,因为Linq->SQL不支持MAX()功能,除非你先将其强制转换到内存中。顺便提一下,尝试使用Linqpad,它可以使这种操作变得更加容易。 - bigamil
啊...很好 :) 我很快就要测试一下。我需要时确实使用LINQPad(一直在用它来测试查询),但这次所需的理论和经验有点超出我的能力范围..因此我在这里发布了问题 :) - Faizan Kazi
太好了!很高兴我能帮到你,伙计。你在巴基斯坦真是太疯狂了。现在的日子真是不可思议,你可以从数千英里之外得到帮助。保重。 - bigamil

2

我可能为解决您的问题已经太晚了,但是我曾经遇到过类似的问题,并且能够使用这样的查询获得所需的结果:

from g in ee.Records
group g by g.CustID into grp
from last in (from custRec in grp where custRec.Id == grp.Max(cr => cr.Id) select custRec)
select last

1

我认为 grp.LastOrDefault() 这个 C# 函数是 SQL 不知道的。LINQ 将您的查询转换为 SQL 查询,以便您的数据库服务器能够理解。您可能想尝试创建一个存储过程,或者另一种方法来过滤您要查找的内容。

您的第二个查询之所以有效,是因为 LINQ to SQL 返回一个列表,然后您在 C# 列表上执行 LINQ 查询(以过滤出您需要的内容),该列表实现了 IEnumerable/IQueryable 接口并理解 grp.LastOrDefault()。


真的,MySQL实体框架提供程序中没有实现LastOrDefault(正如错误所示)。 - Faizan Kazi
是的,它返回一个长列表作为内存集合,然后被过滤成每个CustID的单个条目,这在内存和处理器使用方面并不理想。这就是为什么我在这里寻求帮助的原因 :) - Faizan Kazi

1
如果您将LastOrDefault()替换为简单的Last(),会发生什么? (是的,您将不得不检查记录表是否为空)
因为我看不到MySQL如何返回“默认”组。这不是可以简单地转换为SQL的事情。

当我使用Last时,会出现以下错误:System.NotSupportedException was unhandled by user code Message=LINQ to Entities不认识方法'Faizan_Kazi_Utils.Record LastRecord',并且这个方法无法被转换成存储表达式。 Source=System.Data.Entity - Faizan Kazi

0

我有另一个想法:

// Get a list of all the id's i need by:
// grouping by CustID, and then selecting Max ID from each group.
var distinctLatest = (from x in ee.Records
                          group x by x.CustID into grp 
                          select grp.Max(g => g.id)).ToArray();

// List<Record> result = new List<Record>();

//now we can retrieve individual records using the ID's retrieved above
// foreach (int i in distinctLatest)
// {
//    var res = from g in ee.Records where g.id == i select g;
//    var arr = res.ToArray();
//    result.Add(res.First());
// }

// alternate version of foreach
dgvLatestDistinctRec.DataSource = from g in ee.Records
                                           join i in distinctLatest
                                           on g.id equals i
                                           select g;

这有点基于Bigamil的解决方案...它在我的脑海中引发了这样一系列的思考。 - Faizan Kazi
你真的不想在这个时候使用.ToArray()或.ToList(),因为它会返回所有记录并将它们放入内存中。你希望Linq -> SQL完成所有工作。基本上它会构建一个动态表达式树,直到你请求一个值才会实际执行任何SQL。请求值的关键事项包括(ToList()、ToArray()、First()、Last()等),如果我重复了请原谅。无论如何,我更新了我的答案以确切地满足你的需求。如果它对你有用,请给我点赞/标记我为答案!希望这对你有所帮助。 - bigamil
嗯...但是第一个.ToArray只是将一些整数加载到内存中(即不同的客户ID),然后第二个.ToArray正好做我需要的事情(加载完整记录:每个不同客户的最新记录)...这样不行吗? - Faizan Kazi
哦,还要注意的是,第二个查询(.ToArray)每次只会检索一条记录,因为它是按主键(id)进行检索的,所以不应该一次加载多条记录到内存中。 - Faizan Kazi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接