使用 Linq 对 IQueryable 进行分组、计算不同的 SQL 查询

问题描述 投票:0回答:1

我正在尝试将以下 SQL 转换为 IQueryable,这样我就可以在从数据库检索数据之前访问结果并根据不同条件进行过滤, 但我收到一个错误,无法翻译 LINQ 查询。

 SELECT
    a.Id AS HandymanId,
    u.SubscriptionSource,
    a.TrialExpiryDate,
    COUNT(DISTINCT b.Id) AS PhoneCallCount,
    COUNT(DISTINCT CASE WHEN b.RecordingSid IS NOT NULL AND b.RecordingSid <> '' THEN b.Id END) AS VoiceMessageCount,  
    COUNT(DISTINCT d.Id) AS AppointmentCount,
    COUNT(DISTINCT e.Id) AS TodoCount,
    COUNT(DISTINCT f.Id) AS DocumentationCount,
    COUNT(DISTINCT g.Id) AS MessageCount
FROM
    [dbo].[Handymen] a
LEFT JOIN
    PhoneCalls b ON a.Id = b.HandymanId
LEFT JOIN
    [Identity].[User] u ON u.Id = a.ApplicationUserId
LEFT JOIN
    Projects c ON a.Id = c.HandymanId
LEFT JOIN
    Appointments d ON c.Id = d.ProjectId
LEFT JOIN
    Todos e ON e.ProjectId = c.Id
LEFT JOIN
    Documentations f ON f.ProjectId = c.Id
LEFT JOIN
    [Messages] g ON g.ProjectId = c.Id
GROUP BY
    a.Id, u.SubscriptionSource, a.TrialExpiryDate;
from user in context.Handymen
               join call in context.PhoneCalls on user.Id equals call.HandymanId into callGroup
               join appointment in context.Appointments on user.Id equals appointment.Project.HandymanId into appointmentGroup
               join todo in context.Todos on user.Id equals todo.Project.HandymanId into todoGroup
               join documentation in context.Documentations on user.Id equals documentation.Project.HandymanId into documentationGroup
               join enquiry in context.Projects on user.Id equals enquiry.HandymanId into enquiryGroup
               join message in context.Messages on user.Id equals message.Project.HandymanId into messageGroup
               where user.HandymanProTrialExpiryDate > DateTime.UtcNow
               select new MyData
                   {
                       Id = user.Id,                       
                       CallCount = callGroup.Select(call => call.Id).Distinct().Count(),
                       AppointmentCount = appointmentGroup.Select(appointment => appointment.Id).Distinct().Count(),
                       TodoCount = todoGroup.Select(todo => todo.Id).Distinct().Count(),
                       DocumentationCount = documentationGroup.Select(documentation => documentation.Id).Distinct().Count(),
                       EnquiryCount = enquiryGroup.Select(enquiry => enquiry.Id).Distinct().Count(),
                       MessageCount = messageGroup.Select(message => message.Id).Distinct().Count()
                   }
c# sql .net linq entity-framework-core
1个回答
0
投票

无论 LINQ 是否可以翻译,我认为原始查询都不正确。

因此,您需要预先聚合连接,而不是使用

COUNT(DISTINCT

from user in context.Handymen
where user.HandymanProTrialExpiryDate > DateTime.UtcNow
let calls = context.PhoneCalls
    .Where(call => user.Id == call.HandymanId)
    .GroupBy(call => 0)  // group by empty set
    .Select(g => new { CallCount = g.Count(), VoiceMessageCount = g.Count(c => c.RecordingSid != "") })
select new MyData
{
    Id = user.Id,
    calls.CallCount,
    calls.VoiceMessageCount,
    AppointmentCount = context.Appointments.Where(app => user.Id == app.Project.HandymanId).Count(),
    TodoCount = context.Todos(todo => user.Id == todo.Project.HandymanId).Count(),
    DocumentationCount = context.Documentations.Where(doc => user.Id == doc.Project.HandymanId).Count(),
    MessageCount = context.Messages.Where(msg => user.Id == msg.Project.HandymanId).Count()
}

理想情况下,您将能够使用导航属性:

from user in context.Handymen
where user.HandymanProTrialExpiryDate > DateTime.UtcNow
let calls = user.PhoneCalls
    .GroupBy(call => 0)  // group by empty set
    .Select(g => new { CallCount = g.Count(), VoiceMessageCount = g.Count(c => c.RecordingSid != "") })
select new MyData
{
    Id = user.Id,
    calls.CallCount,
    calls.VoiceMessageCount,
    AppointmentCount = user.Projects.SelectMany(p => p.Appointments).Count(),
    TodoCount = user.Projects.SelectMany(p => p.Todos).Count(),
    DocumentationCount = user.Projects.SelectMany(p => p.Documentations).Count(),
    MessageCount = user.Projects.SelectMany(p => p.Messages).Count()
}

你甚至可以预先聚合

Projects
,这可能会更有效

from user in context.Handymen
where user.HandymanProTrialExpiryDate > DateTime.UtcNow
let calls = user.PhoneCalls
    .GroupBy(call => 0)  // group by empty set
    .Select(g => new { CallCount = g.Count(), VoiceMessageCount = g.Count(c => c.RecordingSid != "") })
let projects = user.Projects
    .Select(p => new
    {
        AppointmentCount = p.Appointments.Count(),
        TodoCount = p.Todos.Count(),
        DocumentationCount = p.Documentations.Count(),
        MessageCount = p.Messages.Count()
    })
    .GroupBy(p => 0)  // group by empty set
    .Select(g => new
    {
        AppointmentCount = g.Sum(p => p.AppointmentCount),
        TodoCount = g.Sum(p => p.TodoCount),
        DocumentationCount = g.Sum(p => p.DocumentationCount),
        MessageCount = g.Sum(p => p.MessageCount)
    })
select new MyData
{
    Id = user.Id,
    calls.CallCount,
    calls.VoiceMessageCount,
    projects.AppointmentCount,
    projects.TodoCount,
    projects.DocumentationCount,
    projects.MessageCount,
}
© www.soinside.com 2019 - 2024. All rights reserved.