【问题标题】:EF Core 2.1, linq query not producing group by sql query and I can see just Order By at lastEF Core 2.1,linq 查询没有按 sql 查询产生分组,我最后只能看到 Order By
【发布时间】:2019-11-09 16:03:39
【问题描述】:

我有一个包含 Group By 子句的 linq 查询,但 Group By 没有在 sql server 上发生。 我尝试了一个简单的查询,并且 Group By 发生在 sql server 上。 请指导我为什么会有这种不同的行为? 我希望在服务器上进行分组以提高性能。

如果我记录 sql 查询,我会在其中得到分组的简单查询:

var testt = (from doc in _patientRepository.Documents
                     group doc by doc.DocumentType into G
                     select new
                     {
                         Key = G.Key

                     }).ToList();

生成的sql:

Executed DbCommand (247ms) [Parameters=[], CommandType='Text', 
CommandTimeout='30']
SELECT [doc].[DocumentType] AS [Key]
FROM [Document] AS [doc]
GROUP BY [doc].[DocumentType]

问题查询:

var patX = (from doc in _patientRepository.Documents
                                               join pat in _patientRepository.Patients
                                               on doc.PatientId.ToString().ToLower() equals pat.PatientId.ToString().ToLower()
                                               where doc.Source.ToLower() != "testclient.server.postman" &&
                                               pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
                                               select new Document()
                                               {
                                                   DocumentId = doc.DocumentId,
                                                   CreationDateTime = doc.CreationDateTime,
                                                   DocumentType = doc.DocumentType,
                                                   PatientId = doc.PatientId,
                                                   DocumentTypeVersion = doc.DocumentTypeVersion,
                                                   Source = doc.Source,
                                                   PayloadLeft = DocumentMapper.DeserializePayload(doc.PayloadLeft),
                                                   PayloadRight = DocumentMapper.DeserializePayload(doc.PayloadRight),
                                                   PayloadBoth = DocumentMapper.DeserializePayload(doc.PayloadBoth),
                                                   IsSalesforceSynced = doc.IsSalesforceSynced,
                                                   HcpId = pat.HcpId
                                               }).GroupBy(p => new { p.PatientId, p.DocumentType })
        .Select(g => g.OrderByDescending(p => p.CreationDateTime).FirstOrDefault())
        .Where(x => x.IsSalesforceSynced == false)
        .ToList();

为什么不生成group-by sql:

Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (200ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [doc].[DocumentId], [doc].[CreationDateTime], [doc].[DocumentType], [doc].[PatientId], [doc].[DocumentTypeVersion], [doc].[Source], [doc].[PayloadLeft], [doc].[PayloadRight], [doc].[PayloadBoth], [doc].[IsSalesforceSynced], [pat].[HcpId]
FROM [Document] AS [doc]
INNER JOIN [Patient] AS [pat] ON LOWER(CONVERT(VARCHAR(36), [doc].[PatientId])) = LOWER(CONVERT(VARCHAR(36), [pat].[PatientId]))
WHERE ((LOWER([doc].[Source]) <> N'testclient.server.postman') AND ([pat].[Deleted] = 0)) AND LOWER([pat].[HcpId]) IN (N'4e7103a9-7dff-4fa5-b540-a32a31be2997', N'abc1', N'def2', N'ghi3')
ORDER BY [doc].[PatientId], [doc].[DocumentType]

我尝试了以下方法,但生成了相同的 sql:

    var patX = ((from doc in _patientRepository.Documents
                                                       join pat in _patientRepository.Patients
                                                       on doc.PatientId.ToString().ToLower() 
                                                       equals pat.PatientId.ToString().ToLower()
                                                       where doc.Source.ToLower() != "testclient.server.postman" &&
                                                       pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
                                                       select new Document()
                                                       {
                                                           DocumentId = doc.DocumentId,
                                                           CreationDateTime = doc.CreationDateTime,
                                                           DocumentType = doc.DocumentType,
                                                           PatientId = doc.PatientId,
                                                           DocumentTypeVersion = doc.DocumentTypeVersion,
                                                           Source = doc.Source,
                                                           PayloadLeft = DocumentMapper.DeserializePayload(doc.PayloadLeft),
                                                           PayloadRight = DocumentMapper.DeserializePayload(doc.PayloadRight),
                                                           PayloadBoth = DocumentMapper.DeserializePayload(doc.PayloadBoth),
                                                           IsSalesforceSynced = doc.IsSalesforceSynced,
                                                           HcpId = pat.HcpId
                                                       }).GroupBy(p => new { p.PatientId, p.DocumentType })
                .Select(g => g.OrderByDescending(p => p.CreationDateTime).FirstOrDefault())
                .Where(x => x.IsSalesforceSynced == false))
                .ToList();

【问题讨论】:

  • 我认为你的问题可能是你的select在中间。你应该最后做select。
  • 第一次选择和分组依据创建组,第二次选择从每个组中获取最新数据。
  • 我不能先分组。我的流程是这样的:选择文档 > 按某些条件对它们进行分组 > 获取每个组的第一个成员。
  • 为什么不能先分组?

标签: entity-framework linq-to-sql entity-framework-core linq-to-entities entity-framework-core-2.1


【解决方案1】:

在 2.1 版之前,在 EF Core 中,GroupBy LINQ 运算符将始终在内存中进行计算。现在支持在大多数常见情况下将其转换为 SQL GROUP BY 子句。

更改代码:尝试将 .GroupBy 放在 .select 方法之前(第一次选择)

【讨论】:

  • 请理解我的问题,第一次查询没有问题。问题出在第二个查询中。查看为两个 linq 查询生成的 sql 查询。
  • @VatanSoni 这个答案的建议和我做的一样——你需要先分组,然后再选择是否要翻译成 SQL。
【解决方案2】:

考虑重新排序查询,以便新类的 select 位于最后:

var p1 = from doc in _patientRepository.Documents
         join pat in _patientRepository.Patients on doc.PatientId.ToString().ToLower() equals pat.PatientId.ToString().ToLower()
         where doc.Source.ToLower() != "testclient.server.postman" && pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
         group new { doc, pat.HcpId } by new { doc.PatientId, doc.DocumentType } into dpg
         select dpg.OrderByDescending(dp => dp.doc.CreationDateTime).FirstOrDefault();

var patX = (from dp in p1
            where !dp.doc.IsSalesforceSynced
            select new Document() {
                DocumentId = dp.doc.DocumentId,
                CreationDateTime = dp.doc.CreationDateTime,
                DocumentType = dp.doc.DocumentType,
                PatientId = dp.doc.PatientId,
                DocumentTypeVersion = dp.doc.DocumentTypeVersion,
                Source = dp.doc.Source,
                PayloadLeft = DocumentMapper.DeserializePayload(dp.doc.PayloadLeft),
                PayloadRight = DocumentMapper.DeserializePayload(dp.doc.PayloadRight),
                PayloadBoth = DocumentMapper.DeserializePayload(dp.doc.PayloadBoth),
                IsSalesforceSynced = dp.doc.IsSalesforceSynced,
                HcpId = dp.HcpId
            })
            .ToList();

【讨论】:

  • 非常感谢,但使用此解决方案也可以将 Group-By 作为内存。
  • @VatanSoni 这令人失望。恐怕您遇到了 EF Core 的限制(这就是为什么我认为它还没有准备好供企业使用)。 EF Core 3.0 有望大大改善这一点。
猜你喜欢
  • 2020-03-29
  • 2017-12-16
  • 1970-01-01
  • 1970-01-01
  • 2012-02-24
  • 2016-11-25
  • 2022-01-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多