LINQ GroupBy,从Count中找到Max(然后是一些...)

时间:2014-10-09 02:43:12

标签: c# linq

我一直试图想出这个问题很长一段时间......

我正在尝试查询公司的许可证使用情况。我有一个捕获数据的服务和一个SQL服务器DB来存储它。我的问题是查询数据。我认为这将是最简单的部分,但不是那么多。

  1. 每五分钟捕获一次数据,并记录捕获时间。
  2. 最大值必须是特定捕获点,但在特定时间范围内(60分钟)
  3. 数据需要按特定字段进行分组。
  4. 用户可以选择应用程序,功能和/或部门,以及查询的开始和结束时间。
  5. 示例数据

    +-------------+---------+----------+------------------+
    | Application | Feature | Division |   RecordedTime   |
    +-------------+---------+----------+------------------+
    | App1        | Feat1   | Div1     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div2     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:05 |
    | App1        | Feat1   | Div2     | 2014-01-01 00:05 |
    | App1        | Feat1   | Div2     | 2014-01-01 00:05 |
    | App1        | Feat2   | Div2     | 2014-01-01 00:10 |
    +-------------+---------+----------+------------------+
    

    结果应为

    Time: 2014-01-01 00:00
    +------+-------+------+---+
    | App1 | Feat1 | Div1 | 4 |
    | App1 | Feat1 | Div2 | 2 |
    | App1 | Feat2 | Div2 | 1 |
    +------+-------+------+---+
    

    这是我到目前为止的代码:(有些是从Stack Overflow上的其他帖子中拼凑出来的)

    var results = context.ViewData
        .Where(x => (x.FeatureID == 1) && 
               StartTimeInput <= x.RecordedDateTime &&
               EndTimeInput >= x.RecordedDateTime)
        .GroupBy(x => new
            {
                Application = x.ApplicationName,
                Feature = x.FeatureName,
                Division = x.Division,
                RecordedDateTime = x.RecordedDateTime
            })
        .Select(x => new
            {
                Application = x.Key.Application,
                Feature = x.Key.Feature,
                Division = x.Key.Division,
                RecordedDateTime = x.Key.RecordedDateTime,
                Count = x.Count()
            })
        .OrderBy(x => x.RecordedDateTime)
        .AsEnumerable().GroupBy(x =>
            {
                var stamp = x.RecordedDateTime;
                stamp = stamp.AddMinutes(-(stamp.Minute % 60));
                return stamp;
            }).Select(x => new
            {
                Stamp = x.Key,
                Data = x .Select(y => new
                {
                    Application = y.Application,
                    Feature = y.Feature,
                    Division = y.Division,
                    Count = y.Count
               })
            });
    

    上面的代码让我更接近,但它并没有为我提供应用,功能和部门每小时最大值。

    更新

    为了澄清,我遇到的麻烦是按小组在60分钟的特定时间点找到一个计数的最大值。

    按样本数据对RecordedTime,Application,Feature,Division进行分组会得到以下结果:

    +------+-------+------+---+------------------+
    | App1 | Feat1 | Div1 | 4 | 2014-01-01 00:00 |
    | App1 | Feat1 | Div2 | 1 | 2014-01-01 00:00 |
    | App1 | Feat1 | Div1 | 1 | 2014-01-01 00:05 |
    | App1 | Feat1 | Div2 | 2 | 2014-01-01 00:05 |
    | App1 | Feat2 | Div2 | 1 | 2014-01-01 00:10 |
    +------+-------+------+---+------------------+
    
    Between the time range 2014-01-01 00:00 and 2014-01-01 01:00, 
    the marked lines should be selected as the maximum value for that time range 
    if you assume that between that time range, none of the grouping exceed that value:
    
       +------+-------+------+---+------------------+
    -->| App1 | Feat1 | Div1 | 4 | 2014-01-01 00:00 |<--
       | App1 | Feat1 | Div2 | 1 | 2014-01-01 00:00 |
       | App1 | Feat1 | Div1 | 1 | 2014-01-01 00:05 |
    -->| App1 | Feat1 | Div2 | 2 | 2014-01-01 00:05 |<--
    -->| App1 | Feat2 | Div2 | 1 | 2014-01-01 00:10 |<--
       +------+-------+------+---+------------------+
    

    如果样本数据是这样的:

    +-------------+---------+----------+------------------+
    | Application | Feature | Division |   RecordedTime   |
    +-------------+---------+----------+------------------+
    | App1        | Feat1   | Div1     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div2     | 2014-01-01 00:00 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:05 |
    | App1        | Feat1   | Div2     | 2014-01-01 00:05 |
    | App1        | Feat1   | Div2     | 2014-01-01 00:05 |
    | App1        | Feat2   | Div2     | 2014-01-01 00:10 |
    .......................................................
    | App1        | Feat1   | Div1     | 2014-01-01 00:40 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:40 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:40 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:40 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:40 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:40 |
    | App1        | Feat1   | Div1     | 2014-01-01 00:40 |
    | App1        | Feat2   | Div2     | 2014-01-01 00:40 |
    | App1        | Feat2   | Div2     | 2014-01-01 00:40 |
    | App1        | Feat2   | Div2     | 2014-01-01 00:40 |
    | App1        | Feat2   | Div2     | 2014-01-01 00:40 |
    | App1        | Feat2   | Div2     | 2014-01-01 00:40 |
    +-------------+---------+----------+------------------+
    
    The grouping and results would look like this:
    
       +------+-------+------+---+------------------+
       | App1 | Feat1 | Div1 | 4 | 2014-01-01 00:00 |
       | App1 | Feat1 | Div2 | 1 | 2014-01-01 00:00 |
       | App1 | Feat1 | Div1 | 1 | 2014-01-01 00:05 |
       | App1 | Feat1 | Div2 | 2 | 2014-01-01 00:05 |
    -->| App1 | Feat2 | Div2 | 1 | 2014-01-01 00:10 |<--
    -->| App1 | Feat1 | Div1 | 7 | 2014-01-01 00:40 |<--
    -->| App1 | Feat2 | Div2 | 5 | 2014-01-01 00:40 |<--
       +------+-------+------+---+------------------+
    

    不同之处在于,在2014-01-01 00:00和01:00之间的60分钟内,来自1区的另外3人在00:40开始使用应用1的功能1,因此最大许可证数量在申请1的特征1的00:00和01:00之间使用变为7。相同的逻辑适用于Feature 2和Division 2,最大值为5。

    希望这有帮助!这就是为什么我长期以来一直在努力解决这个问题,这甚至都不是一个容易解释的问题。

1 个答案:

答案 0 :(得分:0)

您可以在单个查询中执行此操作。如果您更喜欢方法语法,请告诉我,我可以为您翻译。

var query =   from x in context.ViewData
              where x.FeatureID == 1
              where StartTimeInput <= x.RecordedDateTime
              where EndTimeInput >= x.RecordedDateTime
              group x by new { x.ApplicationName, x.FeatureName, x.Division } into x
              select new
              {
                Application = x.Key.ApplicationName,
                Feature = x.Key.FeatureName,
                Division = x.Key.Division,
                Max = (from g in x
                      group g by g.RecordedDateTime into g
                      orderby g.Count() descending
                      select g.Count()).FirstOrDefault()
              };

如果您还想要时间,请将子查询移动到这样的let

...
let max = (from g in x
           group g by g.RecordedDateTime into g
           orderby g.Count() descending
           select g).FirstOrDefault()
select new
{
  ...
  Max = max.Count(),
  DateTime = max.Key
};