Linq2Sql查询 - 使用分组进行数据透视

时间:2011-09-29 01:56:37

标签: linq-to-sql group-by pivot

我有一个需要在gridview中显示的TimeTable的以下数据集。目前,数据集的片段如下所示:

SessionNum    TimeStart    TimeStop    Details
----------    ---------    --------    -------
1             08:00        09:00       Math101
1             09:00        10:00       Comp102
1             11:00        12:00       Engn101
2             08:00        09:00       Comp102
2             09:00        10:00       Math101
2             10:00        11:00       Acco103

总共有5个会话,我希望数据集看起来像:

TimeStart    TimeStop    Session1    Session2     ...
---------    --------    --------    --------     ---
08:00        09:00       Math101     Comp102
09:00        10:00       Comp102     Math101
10:00        11:00       -           Acco103
11:00        12:00       Engn101     -

正如您将看到的,不需要集合功能......只是分组,但对于我的生活,我似乎无法绕过这一个。我有以下LINQ查询生成第一个数据集:

List<TimeTable> list = db.TimeTables.OrderBy(o => o.TimeStart).OrderBy(o => o.SessionNum).ToList();

这很好用,并生成按SessionNum然后TimeStart排序的数据集。我解决这个问题的尝试引发了以下问题:

var result = list.GroupBy(t => t.TimeStart).Select(s => new {
    TimeStart = s.Key,
    Session1 = s.Where(x => x.SessionNum == 1),
    Session2 = s.Where(x => x.SessionNum == 2)
});

这跑了,但遗憾的是没有用。我知道需要一个GroupBy(或一对),但从这一点开始我有点迷失。我真的很感激任何帮助解决这个问题。提前谢谢!

1 个答案:

答案 0 :(得分:5)

您无法在LINQ中直接执行数据透视查询。你可以做的是创建一个这样的结构:

var record = new
{
    TimeStart = "10:00",
    TimeStop = "11:00",
    Sessions = new [] { "-", "Acco103", },
};

如果有这些记录的列表,则必须确保Sessions属性的数组长度与整个数据集中的不同会话数相同。然后,您可以通过索引到数组来访问会话信息。

在查看查询后,这应该更有意义。

首先,在数据库中查询所需数据:

var query =
    from s in db.TimeTables
    orderby s.TimeStop
    orderby s.TimeStart
    group s by new { s.TimeStart, s.TimeStop } into gss
    select new
    {
        gss.Key.TimeStart,
        gss.Key.TimeStop,
        Sessions = gss.ToArray(),
    };

现在确定不同的会话集:

var sessionNums =
    db.TimeTables
        .Select(s => s.SessionNum)
        .Distinct()
        .OrderBy(n => n)
        .ToArray();

现在在内存中处理此数据(请注意.ToArray()上的query调用):

var process =
    from q in query.ToArray()
    let lookup = q.Sessions
        .ToLookup(s => s.SessionNum, s => s.Details)
    select new
    {
        q.TimeStart,
        q.TimeStop,
        Sessions = sessionNums
            .Select(n => String.Join(
                ", ",
                lookup[n].DefaultIfEmpty("-")))
            .ToArray(),
    };

这是艰苦的工作。 lookup创建了一种简单的方法来获取任何SessionNum的会话详细信息。调用lookup[n].DefaultIfEmpty("-")可确保每个会话至少有一个值。 String.Join确保如果源数据同时具有同一会话号的两个会话,那么我们最终会得到一个值。

无论有多少会话,这个结果都是安全的,因为它只会扩展数组。

process查询的输出如下所示:

process-dump

然后你可以做这个查询:

var result =
    from p in process
    select new
    {
        p.TimeStart,
        p.TimeStop,
        Session1 = p.Sessions[0],
        Session2 = p.Sessions[1],
    };

这将有效地“转动”您的结果,但您需要明确地输入每个“SessionX”属性。

result查询的输出如下所示:

result-dump