首页 > 解决方案 > 基于状态使用 group by 计算统计信息的问题

问题描述

我有如下数据:

Table : LeaveRequest

Id    DepartmentId
1     100

Table: LeaveUpdateLogs

Id      RequestedDate             LeaveRequestId     Status
1       2020-01-26 11:55:56       1                  Pending
2       2020-02-24 10:55:56       1                  Accepted
3       2020-02-24 11:55:56       1                  Accepted
4       2020-03-01 09:55:56       1                  Declined
5       2020-03-27 10:55:56       1                  Closed

6       2020-01-09 05:55:56       2                  Pending
6       2020-02-09 05:55:56       2                  Accepted
7       2020-05-12 02:55:56       2                  Accepted
8       2020-06-14 05:55:56       2                  Declined
9       2020-06-15 05:55:56       2                  Closed 

我想计算介于之间的统计信息,Start date and EndDate并想计算每个状态的统计信息。

预期输出:开始日期 = 01-01-2020 结束日期 = 06-30-2020

Pending =  2 (2020-01-26 11:55:56,2020-01-09 05:55:56)
Accepted = 3 (2020-02-24 11:55:56,2020-02-09 05:55:56,2020-05-12 02:55:56)
Declined = 0
Closed = 2 (2020-03-27 10:55:56, 2020-06-15 05:55:56)

课程:

public class LeaveRequest 
    {

        public int Id { get; set; }
        
        public int DepartmentId { get; set; }

        public virtual ICollection<LeaveUpdateLogs> LeaveUpdateLogs { get; set; }

    }
    
    public class LeaveUpdateLogs 
    {
    
        public int Id { get; set; }

        public DateTimeOffset RequestedDate { get; set; }
        
        public int LeaveRequestId { get; set; }
        
        public string Status { get; set; }

        public virtual LeaveRequest LeaveRequest { get; set; }

    }

询问:

var query = from l in context.LeaveUpdateLogs
            where l.LeaveRequest.DepartmentId == 100 &&
            (l.RequestedDate >= fromDate && l.RequestedDate < toDate)

对我来说唯一的挑战是计算同一个月和同一 LeaveRequestId的数据。例如 :

Id      RequestedDate             LeaveRequestId     Status
4       2020-03-01 09:55:56       1                  Declined
5       2020-03-27 10:55:56       1                  Closed

对于上述数据,我们在同一个月有 2 个状态,对于相同的 LeaveRequestId 即 1,但我想考虑该月的最后日期(“2020-03-27”)并为该状态增加 1(“已关闭”)。

我将非常感谢任何帮助:)

标签: c#entity-frameworklinqentity-framework-6

解决方案


看起来您想要“每个 leaveupdatelog leaverequestid-month 的最新状态”然后生成每个状态的统计信息,这对我来说是两个分组。第一个分组剔除无趣的状态,第二个分组计算它们

var interesting = query.GroupBy(lul => new{lul.LeaveRequestId, D=new DateTime(lul.RequestedDate.Year, lul.RequestedDate.Month, 1)})
    .Select(g => g.OrderByDescending(gg => gg.RequestedDate).First());

这将按 ID 和请求日期的月份对数据进行分组。这意味着您得到的组有两个成员分别代表 2,3 和 4,5 和 8,9 然后只选择最新日期的一个,删除 2,4,8

然后我们可以对剩余部分进行另一个分组

var stats = interesting.GroupBy(lul => lul.Status);

这实现了一个集合,其中 stats 中的每个项目都有一个状态键和一个带有数据的 leaveupdatelogs 集合(应该真正将该类重命名为单数,类不应该有复数名称),如下所示:

foreach(var g in stats)
    Console.WriteLine($"{g.Key} = {g.Count} ({string.Join(',', g.Select(gg => gg.RequestedDate))}");

应该会产生您期望的输出,除了没有 Declined 因此不会为它们打印任何内容。如果重要的是不要让他们“通知他们缺席”,那么也许考虑另一种操作来揭示他们,例如

someArrayOfAllStatus.Except(stats.Select(g=>g.Key))

您可以从枚举本身或原始查询(使用 select/distinct)生成所有状态的数组,具体取决于枚举中是否存在您不想显示的其他状态,因为它们不在查询中

重要的是要记住 LINQ 组不像 sql 组。在 sql 中,您必须指定聚合并丢弃数据,因为没有 SELECT 就不能拥有 GROUP BY。在 LINQ 中,您可以,因此组操作实际上形成了键控存储桶并将所有数据作为集合放入其中,因此在任何步骤中,所有原始数据都可以进行操作。换句话说,LINQ 组只是将 X 记录的数据集分解为 Y 多个 Z 记录(其中 Y * Avg(Z) = X),因此您可以迭代每个 Y 并执行诸如“仅取第一个 Z”之类的操作(即我们首先做了什么)或“count and stringjoin all the Z”(这是我们第二个做的)


推荐阅读