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

maG*_*aGz 2 pivot group-by linq-to-sql

我有一个需要在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
Run Code Online (Sandbox Code Playgroud)

总共有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     -
Run Code Online (Sandbox Code Playgroud)

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

List<TimeTable> list = db.TimeTables.OrderBy(o => o.TimeStart).OrderBy(o => o.SessionNum).ToList();
Run Code Online (Sandbox Code Playgroud)

这工作得很好,并产生排序的数据集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)
});
Run Code Online (Sandbox Code Playgroud)

这跑了,但不幸的是没有用.我知道一个GroupBy(或几个)是必需的,但是从这一点开始我有点迷失了.我真的很感激任何帮助解决这个问题.先感谢您!

Eni*_*ity 5

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

var record = new
{
    TimeStart = "10:00",
    TimeStop = "11:00",
    Sessions = new [] { "-", "Acco103", },
};
Run Code Online (Sandbox Code Playgroud)

如果有这些记录的列表,则必须确保该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(),
    };
Run Code Online (Sandbox Code Playgroud)

现在确定不同的会话集:

var sessionNums =
    db.TimeTables
        .Select(s => s.SessionNum)
        .Distinct()
        .OrderBy(n => n)
        .ToArray();
Run Code Online (Sandbox Code Playgroud)

现在在内存中处理这些数据(注意.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(),
    };
Run Code Online (Sandbox Code Playgroud)

这是艰苦的工作.将lookup创建一个简单的方法来获取会话细节进行任何SessionNum.调用lookup[n].DefaultIfEmpty("-")确保每个会话至少有一个值.在String.Join确保如果源数据有两会在我们结束了一个价值,同时在同一会话数.

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

process查询的输出如下所示:

过程转储

然后你可以做这个查询:

var result =
    from p in process
    select new
    {
        p.TimeStart,
        p.TimeStop,
        Session1 = p.Sessions[0],
        Session2 = p.Sessions[1],
    };
Run Code Online (Sandbox Code Playgroud)

这将有效地"转动"您的结果,但您需要明确地放入每个"SessionX"属性.

result查询的输出如下所示:

结果转储