使用linq group by语句作为子查询

时间:2013-11-01 16:43:34

标签: c# linq

我有以下查询可以正常工作:

var bands = new List<TimeBand>()
            {
                new TimeBand(){Region = 10,PeriodId = 5,StartDate = new DateTime(2013, 04, 01),EndDate = new DateTime(2014, 05, 31),DayName = "Friday",StartTime = "00:00",EndTime = "07:00"},
                new TimeBand(){Region = 10,PeriodId = 5,StartDate = new DateTime(2013, 04, 01),EndDate = new DateTime(2013, 05, 31),DayName = "Friday",StartTime = "07:00",EndTime = "00:00"},
                new TimeBand(){Region = 10,PeriodId = 4,StartDate = new DateTime(2013, 06, 01),EndDate = new DateTime(2013, 08, 31),DayName = "Saturday",StartTime = "20:00",EndTime = "00:00"}
            };

    var query = (from x in bands
                group x by new {x.Region, x.DayName}
                into grp
                    select new TimeBand()
                    {
                        Region = grp.Key.Region,
                        DayName = grp.Key.DayName,
                        StartDate = grp.Min(x => x.StartDate),
                        EndDate = grp.Max(x => x.EndDate)
                    }).ToList();

但是当我按区域和日期名称对结果进行分组时,我的结果中没有得到其他列,即StartTime和EndTime。

如果这是一个SQL查询,我会在子查询中使用这个分组结果并获取其他列。

有没有办法修改这个,所以我也得到了不包含在group by语句中的属性。

由于

4 个答案:

答案 0 :(得分:1)

对源项目进行分组后,您将拥有一系列组。您将如何投影这些组取决于您。通常,您在每个组上选择分组键和一些聚合值(这就是SQL的工作方式)。但您可以选择每个组本身,或每个组中的第一个项目,或最后一个组项目中的某个值:

from b in bands
group b by new { b.Region, b.DayName } into g
select new {
   g.Key.Region,
   g.Key.DayName,
   StartDate = g.Min(x => x.StartDate),
   EndDate = g.Max(x => x.EndDate),
   AllBandsFromGroup = g,
   FirstBand = g.First(),
   LastBandPeriod = g.Last().Period
}

答案 1 :(得分:1)

如果您想要密钥以及组中所有项目的所有值,只需选择整个组:

select grp;

另一个选项是从组中选择所需列的所有值的序列:

select new TimeBand()
{
    Region = grp.Key.Region,
    DayName = grp.Key.DayName,
    StartDates = grp.Select(x => x.StartDate),
    EndDates = grp.Select(x => x.EndDate)
}

如果您不想下载其他一些列的数据,则只需执行此操作。如果您想要所有列,只需使用第一个选项。

答案 2 :(得分:1)

var query = (from x in bands
                     group x by new { x.Region, x.DayName }
                         into grp
                         select new 
                         {
                             Region = grp.Key.Region,
                             DayName = grp.Key.DayName,
                             MinStartDate = grp.Min(x => x.StartDate),
                             AllStartDates = grp.Select(k => k.StartDate).ToList(),
                             EndDate = grp.Max(x => x.EndDate),
                             AllEndDates = grp.Select(k => k.EndDate).ToList(),
                         }).ToList();

答案 3 :(得分:0)

您似乎正在尝试查找每个组的最短开始日期时间以及最长结束日期时间。如果将日期和时间组合到单个属性中,这可能会容易得多。

否则,您需要IComparer<TimeBand>作为开始日期和时间,另一个作为结束日期和时间。

如果您不想结合日期和时间,可以编写方法来获取组合值:

public DateTime GetStart()
{
    int hour = int.Parse(StartTime.Substring(0, 2));
    int minute = int.Parse(StartTime.Substring(3, 2));

    return new DateTime(StartDate.Year, StartDate.Month, StartDate.Day, hour, minute, 0);
}

public DateTime GetEnd()
{
    int hour = int.Parse(EndTime.Substring(0, 2));
    int minute = int.Parse(EndTime.Substring(3, 2));

    return new DateTime(EndDate.Year, EndDate.Month, EndDate.Day, hour, minute, 0);
}

现在您可以对波段进行分组,并为每个组找到最小开始和最大结束:

var query = from x in bands
            group x by new
                       {
                           x.Region,
                           x.DayName
                       }
            into grp
            select new TimeBand()
                   {
                       Region = grp.Key.Region,
                       DayName = grp.Key.DayName,
                       StartDate = grp.Min(x => x.StartDate),
                       StartTime = grp.Min(x => x.GetStart()).ToShortTimeString(),
                       EndDate = grp.Max(x => x.EndDate),
                       EndTime = grp.Max(x => x.GetEnd()).ToShortTimeString(),
                   };

然而,这不是很优雅。我宁愿首先将日期和时间结合起来。