C# LINQ到SQL中的数据透视表

C# LINQ到SQL中的数据透视表,c#,sql-server,linq,linq-to-sql,pivot-table,C#,Sql Server,Linq,Linq To Sql,Pivot Table,由于某些兼容问题,我无法在SQL server中使用Pivot,因此我尝试在代码级别使用LINQ to SQL来完成相同的工作 我有一个如下所示的记录集 Dates RM DM LocationNum City State Count ----- -- -- ----------- ---- ----- ----- 2013-12-13 1 1

由于某些兼容问题,我无法在SQL server中使用
Pivot
,因此我尝试在代码级别使用LINQ to SQL来完成相同的工作

我有一个如下所示的记录集

Dates           RM      DM      LocationNum     City        State   Count
-----           --      --      -----------     ----        -----   -----
2013-12-13      1       1       4795            Grapevine   TX      1
2013-12-13      1       2       4796            Grapevine   TX      1
2013-12-14      2       3       4797            Grapevine   TX      1
2013-12-15      NULL    NULL    NULL            NULL        NULL    0
2013-12-16      NULL    NULL    NULL            NULL        NULL    0   
我正试图将其转换为

RM  DM  Loc     City        2013-12-13      2013-12-14  2013-12-15  2013-12-16  
--  --  ---     ----        ----------      ----------  ----------  ----------
1   1   4795    City1       1               0           0           0
1   2   4796    City2       1               0           0           0
1   3   4797    City3       0               1           0           0

有人能帮我吗。

您将无法使用LINQ to SQL执行整个查询,因为每一列对应于结果类型上的一个属性,并且有未知数量的列,因此无法定义结果类型。我在类似情况下所做的是定义如下类型:

public class PseudoPivotRow
{
    public string RM {get; set;}
    public string DM {get; set;}
    public int Loc {get; set;}
    public string City {get; set;}
    public Dictionary<DateTime, int> CountsByDate{get; set} 
}
ToList
强制执行查询,因此下一部分发生在客户机(C#)端,我们可以将其投影到字典中:

var pivotResult = 
  materializedGroups
    .Select(mg => new PseudoPivotRow
    {
        RM = mg.Key.RM,
        DM = mg.Key.DM,
        Loc = mg.Key.LocationNum,
        CountsByDate = mg.GroupBy(r => r.Dates, r => r.Count)
                        .ToDictionary(g => g.Key, g => g.Sum())
    };
(如果需要,可以在第一个查询中对日期/计数进行分组;我认为这样编写要简单得多,但性能可能更差。)


这种模式的缺点是“pivot”中的每个“行”可能有不同数量的列,并且顺序未定义。如果您需要一致性,一个简单的选择是可以获得所有日期的列表,然后循环使用
pivotResult
,并为字典中没有的每个日期插入0

这里有一个小小的扩展方法,我用它来做你提到的事情。Thios是一个pivot函数,它接受一个委托,并在内存中有数据后进行一些很好的分组:

public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot<TSource, TKey1, TKey2, TValue>
    (
        this IEnumerable<TSource> source, 
        Func<TSource, TKey1> key1Selector, 
        Func<TSource, TKey2> key2Selector,
        Func<IEnumerable<TSource>, TValue> aggregate
    )
{
    return source.GroupBy(key1Selector).Select(
    x => new
    {
        X = x.Key,
        Y = x.GroupBy(key2Selector).Select(
        z => new
        {
            Z = z.Key,
            V = aggregate(z)
        }
        ).ToDictionary(e => e.Z, o => o.V)
    }
    ).ToDictionary(e => e.X, o => o.Y);
}
玩转它,找出符合我们标准的用法。它非常灵活,我已经在一系列不同的场景中使用过它,在这些场景中,其他循环方法几乎不可能做到这一点

[btw]-您可以在我早就开始的线程上找到更多linq小技巧,包括这一个-这里:

public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot<TSource, TKey1, TKey2, TValue>
    (
        this IEnumerable<TSource> source, 
        Func<TSource, TKey1> key1Selector, 
        Func<TSource, TKey2> key2Selector,
        Func<IEnumerable<TSource>, TValue> aggregate
    )
{
    return source.GroupBy(key1Selector).Select(
    x => new
    {
        X = x.Key,
        Y = x.GroupBy(key2Selector).Select(
        z => new
        {
            Z = z.Key,
            V = aggregate(z)
        }
        ).ToDictionary(e => e.Z, o => o.V)
    }
    ).ToDictionary(e => e.X, o => o.Y);
}
var pivotResult = itemsFromPreviousQuery.Pivot(s => s.SeasonID, 
                s => s.FundPropertyEntity.PropertyEntity.PropertyName, 
                lst => lst.Count());