简体   繁体   中英

SQL How to pivot two columns of data into different columns?

This is the table I have:

| Scheme Code | MonthYear | Revenue | Revenue2 |
|-------------|-----------|---------|----------|
| 18VDA       | 2018.1    | 100     | 50       |
| 18VDA       | 2018.2    | 200     | 100      |
| 18VDA       | 2018.3    | 200     | 150      |

and I want to pivot it to like this:

| Scheme Code | 2018.1 A | 2018.2 A | 2018.3 A | 2018.1 B | 2018.2 B | 2018.3 B |
|-------------|----------|----------|----------|----------|----------|----------|
| 18VDA       | 100      | 200      | 200      | 50       | 100      | 150      |

How do I do it so that it pivots in MonthYear, but it duplicates it for both Revenue and Revenue2?

Thanks

EDIT: Messed up the output table I was hoping for! I've edited the actual output table I want to see!

EDIT 2:

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

Select @cols = STUFF((SELECT ',' + QUOTENAME([MonthYear]) 
                    from tableA
                    group by [MonthYear]
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT *
             FROM ( SELECT [Scheme Code], MonthYear ,[Revenue]
                    FROM TableA
                    ) a
              PIVOT(sum(Revenue) for MonthYear in (' + @cols + ') 
                       ) as RevenueMonth
              ORDER BY [Scheme Code]'    

 execute(@query);

This code I wrote will do it for just one column, and I get the output like this:

| Scheme Code | 2018.1 | 2018.2 | 2018.3 |
|-------------|--------|--------|--------|
| 18VDA       | 100    | 200    | 200    |

My suggestion always is to try to write your query as a hard-coded or static version first before diving into dynamic SQL. This let's you get the final result you want with a smaller subset of data and you can verify that you have the logic correct.

I would tackle this by performing an UNPIVOT of the two Revenue columns first, then look at applying the PIVOT function. To UNPIVOT you can use either the UNPIVOT function or you can use CROSS APPLY with a UNION ALL to convert your two Revenue columns into a single column. A static version of the query would be similar to this:

select *
from
(
    select 
        t.[Scheme Code],
        new_colname = concat(t.[MonthYear], ' ', r.colname),
        r.colvalue
    from yourtable t
    cross apply
    (
        select 'A', Revenue union all
        select 'B', Revenue2
    ) r (colname, colvalue)
) d
pivot 
(
    sum(colvalue)
    for new_colname in ([2018.1 A], [2018.2 A], [2018.3 A], [2018.1 B], [2018.2 B], [2018.3 B])
) p;

You'll notice that in the CROSS APPLY I added a column with the A or B that I use to identify either the Revenue or Revenue2 columns. This is then used to create the new column names for the PIVOT .

This should generate the result you want. Now to do this dynamically, you just need to convert the SQL to dynamic code. You can use the following to get the result:

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

Select @cols = STUFF((SELECT ',' + QUOTENAME(concat([MonthYear], x.col)) 
                    from yourtable
                    cross join (select col = ' A' union all select ' B') x
                    group by [MonthYear], x.col
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT *
             FROM 
             ( 
                select 
                    t.[Scheme Code],
                    new_colname = concat(t.[MonthYear], '' '', r.colname),
                    r.colvalue
                from yourtable t
                cross apply
                (
                    select ''A'', Revenue union all
                    select ''B'', Revenue2
                ) r (colname, colvalue)
              ) a
              PIVOT
              (
                sum(colvalue) for new_colname in (' + @cols + ') 
              ) as x
              ORDER BY [Scheme Code]';

exec sp_executesql @query;

Both of these should generate the same results ( dbfiddle demo )

Do it with CASE and dynamic sql.

DECLARE @colsA AS NVARCHAR(MAX),
        @colsB AS NVARCHAR(MAX),
        @query AS NVARCHAR(MAX)

select @colsA = (SELECT ', sum(case [MonthYear] when ''' + [MonthYear] + ''' then Revenue end)' + QUOTENAME([MonthYear] + ' A')
                    from tableA
                    group by [MonthYear]
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'),
       @colsB = (SELECT ', sum(case [MonthYear] when ''' + [MonthYear] + ''' then Revenue2 end)' + QUOTENAME([MonthYear] + ' B')
                    from tableA
                    group by [MonthYear]
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)');

Set @query = 'select [Scheme Code]' + @colsA + @colsB + ' from  TableA  group by [Scheme Code] order by [Scheme Code];';
print @query;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM