SQL Server : dynamic pivot over 5 columns SQL Server : dynamic pivot over 5 columns sql sql

SQL Server : dynamic pivot over 5 columns


In order to get the result, you will need to look at unpivoting the data in the Total and Volume columns first before applying the PIVOT function to get the final result. My suggestion would be to first write a hard-coded version of the query then convert it to dynamic SQL.

The UNPIVOT process converts these multiple columns into rows. There are a few ways to UNPIVOT, you can use the UNPIVOT function or you can use CROSS APPLY. The code to unpivot the data will be similar to:

select id,     col = cast(t_year as varchar(4))+'_'+t_type+'_'+col,     valuefrom ATM_TRANSACTIONS tcross apply(    select 'total', total union all    select 'volume', volume) c (col, value);

This gives you data in the format:

+-----+---------------+-------+| id  |      col      | value |+-----+---------------+-------+| DD1 | 2008_A_total  |  1000 || DD1 | 2008_A_volume |    10 || DD1 | 2008_B_total  |  2000 || DD1 | 2008_B_volume |    20 || DD1 | 2008_C_total  |  3000 || DD1 | 2008_C_volume |    30 |+-----+---------------+-------+

Then you can apply the PIVOT function:

select ID,     [2008_A_total], [2008_A_volume], [2008_B_total], [2008_B_volume],    [2008_C_total], [2008_C_volume], [2009_A_total], [2009_A_volume]from(    select id,         col = cast(t_year as varchar(4))+'_'+t_type+'_'+col,         value    from ATM_TRANSACTIONS t    cross apply    (        select 'total', total union all        select 'volume', volume    ) c (col, value)) dpivot(    max(value)    for col in ([2008_A_total], [2008_A_volume], [2008_B_total], [2008_B_volume],                [2008_C_total], [2008_C_volume], [2009_A_total], [2009_A_volume])) piv;

Now that you have the correct logic, you can convert this to dynamic SQL:

DECLARE @cols AS NVARCHAR(MAX),    @query  AS NVARCHAR(MAX)select @cols = STUFF((SELECT ',' + QUOTENAME(cast(t_year as varchar(4))+'_'+t_type+'_'+col)                     from ATM_TRANSACTIONS t                    cross apply                    (                        select 'total', 1 union all                        select 'volume', 2                    ) c (col, so)                    group by col, so, T_TYPE, T_YEAR                    order by T_YEAR, T_TYPE, so            FOR XML PATH(''), TYPE            ).value('.', 'NVARCHAR(MAX)')         ,1,1,'')set @query = 'SELECT id,' + @cols + '             from             (                select id,                     col = cast(t_year as varchar(4))+''_''+t_type+''_''+col,                     value                from ATM_TRANSACTIONS t                cross apply                (                    select ''total'', total union all                    select ''volume'', volume                ) c (col, value)            ) x            pivot             (                max(value)                for col in (' + @cols + ')            ) p 'execute sp_executesql @query;

This will give you a result:

+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+| id  | 2008_A_total | 2008_A_volume | 2008_B_total | 2008_B_volume | 2008_C_total | 2008_C_volume | 2009_A_total | 2009_A_volume | 2009_B_total | 2009_B_volume | 2009_C_total | 2009_C_volume |+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+| DD1 |         1000 |            10 |         2000 |            20 |         3000 |            30 |         4000 |            40 |         5000 |            50 |         6000 |            60 || DD2 |         7000 |            70 |         8000 |            80 |         9000 |            90 |        10000 |           100 |        11000 |           110 |         1200 |           120 |+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+


declare @stmt nvarchar(max)select @stmt = isnull(@stmt + ', ', '') +         'sum(case when T_YEAR = ''' + T.T_YEAR + ''' and T_TYPE = ''' + T.T_TYPE + ''' then TOTAL else 0 end) as ' + quotename(T.T_YEAR + '_' +  T.T_TYPE + '_TOTAL') + ',' +        'sum(case when T_YEAR = ''' + T.T_YEAR + ''' and T_TYPE = ''' + T.T_TYPE + ''' then VOLUME else 0 end) as ' + quotename(T.T_YEAR + '_' +  T.T_TYPE + '_VOLUME')from (select distinct T_YEAR, T_TYPE from ATM_TRANSACTIONS) as Torder by T_YEAR, T_TYPEselect @stmt = '    select        ID, ' + @stmt + ' from ATM_TRANSACTIONS group by ID'exec sp_executesql    @stmt = @stmt

unfortunately, sqlfiddle.com is not working at the moment, so I cannot create an example for you.

The query created by dynamic SQL would be:

select    ID,    sum(case when T_YEAR = '2008' and T_TYPE = 'A' then TOTAL else 0 end) as 2008_A_TOTAL,    sum(case when T_YEAR = '2008' and T_TYPE = 'A' then VOLUME else 0 end) as 2008_A_VOLUME,    ...from ATM_TRANSACTIONSgroup by ID


Please try:

DECLARE @pivv NVARCHAR(MAX),@Query NVARCHAR(MAX)SELECT @pivv=COALESCE(@pivv+',','')+ QUOTENAME(T_YEAR+'_'+T_TYPE+'_TOTAL')+','+QUOTENAME(T_YEAR+'_'+T_TYPE+'_VOLUME') from ATM_TRANSACTIONS GROUP BY T_YEAR, T_TYPEIF ISNULL(@pivv, '')<>''    SET @Query='SELECT * FROM(        SELECT ID, T_YEAR+''_''+T_TYPE+''_TOTAL'' TYP, TOTAL VAL from ATM_TRANSACTIONS UNION        SELECT ID, T_YEAR+''_''+T_TYPE+''_VOLUME'' TYP, VOLUME VAL from ATM_TRANSACTIONS         )x pivot (SUM(VAL) for TYP in ('+@pivv+')) as xx'IF ISNULL(@Query, '')<>''    EXEC (@Query)