Linq 交叉表和分组

Linq crosstab and grouping

提问人:Gordon Copestake 提问时间:2/22/2013 最后编辑:ArionGordon Copestake 更新时间:2/25/2013 访问量:3745

问:

我有一组数据,如下所示:

ItemID | ProcessID | Sequence
Item 1 | ProcessA  | 1 
Item 1 | ProcessB  | 2 
Item 3 | ProcessA  | 1 
Item 2 | ProcessA  | 1 
Item 1 | ProcessC  | 3
Item 3 | ProcessB  | 2

我想创建一个 linq 查询,该查询将数据格式化为如下所示:

ItemID | Sequence 1 | Sequence 2 | Sequence 3 | Sequence N 
Item 1 | ProcessA   | ProcessB   | ProcessC 
Item 2 | ProcessA   |  
Item 3 | ProcessA   | ProcessB

有没有一种简单的方法可以做到这一点?

C# asp.net LINQ 实体框架 透视

评论

0赞 Hamlet Hakobyan 2/22/2013
可以将 LINQ 扩展库用于数据。PIVOT
0赞 Arion 2/22/2013
@HamletHakobyan:这适用于动态列数吗?我所看到的是他必须对动态sql做一个PIVOT
0赞 Arion 2/22/2013
如果你想用动态的列数来做,我可以看到什么。你必须在 sql 中做到这一点。您使用的是什么 RDMS (mssql)?
0赞 Gordon Copestake 2/22/2013
是的,MSSQL。我想我可以猜测一下如果它使 Linq 工作的最大列数?
0赞 Arion 2/22/2013
或者你可以使用纯sql的方式。你可以只用你拥有的序列数量来制作一个。否则,您将有很多列可能是空的,并且可能更难以良好的方式呈现?Pivot

答:

1赞 Gordon Copestake 2/22/2013 #1

我假设了避免动态列的进程数。不理想,但它适用于我的应用程序。

public static IQueryable GetProcessesForAllItems()
{
    var ctx = new CS3Entities();

    var allprocesses = (from pi in ctx.ProcessesItems
                        orderby pi.SequenceNumber descending
                        select new
                                   {
                                       pi.ItemID,
                                       pi.ProcessID,
                                       pi.SequenceNumber
                                   });

    var query = allprocesses
        .GroupBy(c => c.ItemID)
        .Select(g => new
                         {
                             ItemId = g.Key,
                             Seq100 = g.FirstOrDefault(c => c.SequenceNumber == 100).ProcessID,
                             Seq200 = g.FirstOrDefault(c => c.SequenceNumber == 200).ProcessID,
                             Seq300 = g.FirstOrDefault(c => c.SequenceNumber == 300).ProcessID,
                             Seq400 = g.FirstOrDefault(c => c.SequenceNumber == 400).ProcessID,
                             Seq500 = g.FirstOrDefault(c => c.SequenceNumber == 500).ProcessID,
                             Seq600 = g.FirstOrDefault(c => c.SequenceNumber == 600).ProcessID,
                             Seq700 = g.FirstOrDefault(c => c.SequenceNumber == 700).ProcessID,
                             Seq800 = g.FirstOrDefault(c => c.SequenceNumber == 800).ProcessID,
                             Seq900 = g.FirstOrDefault(c => c.SequenceNumber == 900).ProcessID,
                             //etc
                         });
    return query;

}
1赞 Arion 2/22/2013 #2

我会使用纯sql方法来解决这个问题。所以这里有一个建议给你:

测试数据

CREATE TABLE #T
(
    ItemID VARCHAR(100),
    ProcessID VARCHAR(100), 
    Sequence INT
)
INSERT INTO #T
VALUES
    ('Item 1','ProcessA',1), 
    ('Item 1','ProcessB',2),
    ('Item 3','ProcessA',1),
    ('Item 2','ProcessA',1),
    ('Item 1','ProcessC',3),
    ('Item 3','ProcessB',2)

透视表的列

DECLARE @cols VARCHAR(MAX)
;WITH CTE
AS
(
SELECT
    ROW_NUMBER() OVER(PARTITION BY Sequence ORDER BY Sequence) AS RowNbr,
    'Sequence '+CAST(t.Sequence AS VARCHAR(10)) AS Sequence
FROM
    #T AS t
)
SELECT @cols=STUFF
(
    (
        SELECT
            ',' +QUOTENAME([Sequence])
        FROM
            CTE
        WHERE CTE.RowNbr=1
        FOR XML PATH('')
    )
,1,1,'')

执行动态透视

DECLARE @query NVARCHAR(4000)=
N'SELECT
    *
FROM
(
    SELECT
        t.ItemID,
        t.ProcessID,
        ''Sequence ''+CAST(t.Sequence AS VARCHAR(10)) AS Sequence
    FROM
        #T AS t
) AS SourceTable
PIVOT
(
    MAX(ProcessID)
    FOR Sequence IN ('+@cols+')
) AS p'

执行(@query)

删除临时表

DROP TABLE #T