如何将行值转换为动态列计数的列?

时间:2013-12-04 13:56:55

标签: sql-server tsql dynamic sql-server-2012 pivot

我有以下数据:

DECLARE @DataSource TABLE
(
     [ColumnA] INT
    ,[ColumnB] INT
    ,[ColumnC] INT
)

INSERT INTO @DataSource ([ColumnA], [ColumnB], [ColumnC])
VALUES   (5060,1006,100118)
        ,(5060,1006,100119)
        ,(5060,1006,100120)
        ,(5060,1007,100121)
        ,(5060,1007,100122)
        ,(5060,1012,100123)

SELECT [ColumnA]
      ,[ColumnB]
      ,[ColumnC]
FROM @DataSource

enter image description here

我需要像这样转换:

enter image description here

困难的部分是数据是动态的(我不知道我将拥有多少列)并且我无法在此使用标准数据透视,因为ColumnC中的值不同而且因此我将在ColumnC中显示尽可能多的列。

有没有什么技术可以达到这个目的? 任何形式的帮助(答案,文章,建议)将不胜感激。

1 个答案:

答案 0 :(得分:5)

我建议您在使用PIVOT时始终首先使用硬编码的值编写查询,然后您可以轻松地将查询转换为动态解决方案。

由于您将有多个columnC值将转换为列,因此您需要查看使用row_number()窗口函数为每个{{1}生成唯一序列}基于columnccolumnA的值。

您的查询的起点是:

columnB

Demo。此查询将生成新列名称select [ColumnA], [ColumnB], [ColumnC], 'SampleTitle'+ cast(row_number() over(partition by columna, columnb order by columnc) as varchar(10)) seq from DataSource; 等的列表:

SampleTitle1

然后,您可以使用| COLUMNA | COLUMNB | COLUMNC | SEQ | |---------|---------|---------|--------------| | 5060 | 1006 | 100118 | SampleTitle1 | | 5060 | 1006 | 100119 | SampleTitle2 | | 5060 | 1006 | 100120 | SampleTitle3 | 中列出的新列名称在columnC上应用数据透视:

seq

请参阅SQL Fiddle with Demo

一旦拥有了正确的逻辑,就可以将数据转换为动态SQL。这里的关键是生成新列名列表。我通常使用select columnA, columnB, SampleTitle1, SampleTitle2, SampleTitle3 from ( select [ColumnA], [ColumnB], [ColumnC], 'SampleTitle'+ cast(row_number() over(partition by columna, columnb order by columnc) as varchar(10)) seq from DataSource ) d pivot ( max(columnc) for seq in (SampleTitle1, SampleTitle2, SampleTitle3) ) piv; ,类似于:

FOR XML PATH

Demo。获得列名列表后,您将生成要执行的sql字符串,完整代码将为:

select STUFF((SELECT distinct ',' + QUOTENAME(seq) 
                from
                (
                  select 'SampleTitle'+
                    cast(row_number() over(partition by columna, columnb
                                            order by columnc) as varchar(10)) seq
                  from DataSource
                ) d
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')

SQL Fiddle with Demo。结果如下:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(seq) 
                    from
                    (
                      select 'SampleTitle'+
                        cast(row_number() over(partition by columna, columnb
                                                order by columnc) as varchar(10)) seq
                      from DataSource
                    ) d
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT columnA, ColumnB,' + @cols + ' 
             from 
             (
               select [ColumnA],
                [ColumnB],
                [ColumnC],
                ''SampleTitle''+
                  cast(row_number() over(partition by columna, columnb
                                          order by columnc) as varchar(10)) seq
               from DataSource
            ) x
            pivot 
            (
                max(columnc)
                for seq in (' + @cols + ')
            ) p '

execute sp_executesql @query;