1

I have following result from my sql query:

Id         atr1       atr2       atr3       atr4

1          a          bsdf       csdfs      djk
5          esdds      f          gds        hkkj
8          i          j          ksd        lk
9          ads        sdf        dfse       wer  

Now I need this above result in following format:

S.no   1        2         3      4
Id     1        5         8      9
atr1   a        esdds     i      ads 
atr2   bsdf     f         j      sdf 
atr3   csdfs    gds       ksd    dfse
atr4   djk      hkkj      lk     wer

I am unable to do it with Pivot and Unpivot.

Taryn
  • 242,637
  • 56
  • 362
  • 405
Dusht
  • 4,712
  • 3
  • 18
  • 24
  • So many similar posts on SO .... check http://stackoverflow.com/questions/15745042/efficiently-convert-rows-to-columns-in-sql-server-2008 – rags Sep 04 '13 at 11:19
  • [Search for SQL Server Pivot](http://stackoverflow.com/search?q=sql+server+pivot) delivers 1758 answers **already** present on SO - have a look at those first ... – marc_s Sep 04 '13 at 11:22
  • I don't have fixed no. of rows. – Dusht Sep 04 '13 at 11:31

1 Answers1

5

In order to get the final result that you want you will want to unpivot your current columns and then apply the pivot function. But before you unpivot/pivot your data, I would suggest using row_number() to generate a unique value for each row.

There are a few different ways that you can get the result including using an aggregate function with CASE expression, static PIVOT and dynamic PIVOT.

Aggregate with CASE: You can get the result by first using a UNION ALL query to convert the multiple columns into rows, and then use an aggregate function with a CASE expression:

;with cte as
(
  select id, atr1, atr2, atr3, atr4,
    row_number() over(order by id) seq
  from yourtable
) 
select s_no,
  max(case when seq = 1 then value end) [1],
  max(case when seq = 2 then value end) [2],
  max(case when seq = 3 then value end) [3],
  max(case when seq = 4 then value end) [4]
from
(
  select seq, s_no = 'id', value = cast(id as varchar(5)), so = 1
  from cte
  union all
  select seq, s_no = 'atr1', value = atr1, so = 2
  from cte
  union all
  select seq, s_no = 'atr2', value = atr2, so = 3
  from cte
  union all
  select seq, s_no = 'atr3', value = atr3, so = 4
  from cte
  union all
  select seq, s_no = 'atr4', value = atr4, so = 5
  from cte
) d
group by s_no, so
order by so;

See SQL Fiddle with Demo

Static UNPIVOT/PIVOT: If you have a limited number of values that you want to transform then you can hard-code the query. The process of unpivot is going to convert your multiple columns id, atr1, atr2, atr3, and atr4 and convert them into multiple rows. You did not specify what version of SQL Server you are using but this can be done using the UNPIVOT function or using CROSS APPLY.

select seq, s_no, value, so
from
(
  select id, atr1, atr2, atr3, atr4,
    row_number() over(order by id) seq
  from yourtable
) s
cross apply
(
  select 'id', cast(id as varchar(5)), 1 union all
  select 'atr1', atr1, 2 union all
  select 'atr2', atr2, 3 union all
  select 'atr3', atr3, 4 union all
  select 'atr4', atr4, 5
) c (s_no, value, so);

See SQL Fiddle with Demo. I used CROSS APPLY with a UNION ALL to select each of your columns and convert them into multiple rows. This query will get your data into the following format:

| SEQ | S_NO | VALUE | SO |
|   1 |   id |     1 |  1 |
|   1 | atr1 |     a |  2 |
|   1 | atr2 |  bsdf |  3 |
|   1 | atr3 | csdfs |  4 |
|   1 | atr4 |   djk |  5 |
|   2 |   id |     5 |  1 |

Once you have the data into multiple rows, then you can apply the PIVOT function:

select s_no, [1], [2], [3], [4]
from
(
  select seq, s_no, value, so
  from
  (
    select id, atr1, atr2, atr3, atr4,
      row_number() over(order by id) seq
    from yourtable
  ) s
  cross apply
  (
    select 'id', cast(id as varchar(5)), 1 union all
    select 'atr1', atr1, 2 union all
    select 'atr2', atr2, 3 union all
    select 'atr3', atr3, 4 union all
    select 'atr4', atr4, 5
  ) c (s_no, value, so)
) d
pivot
(
  max(value)
  for seq in ([1], [2], [3], [4])
) piv
order by so;

See SQL Fiddle with Demo.

Dynamic UNPIVOT/PIVOT: The above works great if you had an known or limited number of new columns that you want to create but if you have an unknown number of values to convert into columns, then you will want to look at using dynamic SQL. This will generate a sql string that will then be executed to get you the final result:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(seq) 
                    from 
                    (
                      select row_number() over(order by id) seq
                      from yourtable
                    )d
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT s_no,' + @cols + ' 
            from
            (
              select seq, s_no, value, so
              from
              (
                select id, atr1, atr2, atr3, atr4,
                  row_number() over(order by id) seq
                from yourtable
              ) s
              cross apply
              (
                select ''id'', cast(id as varchar(5)), 1 union all
                select ''atr1'', atr1, 2 union all
                select ''atr2'', atr2, 3 union all
                select ''atr3'', atr3, 4 union all
                select ''atr4'', atr4, 5
              ) c (s_no, value, so)
            ) x
            pivot 
            (
                max(value)
                for seq in (' + @cols + ')
            ) p '

execute sp_executesql @query;

See SQL Fiddle with Demo. All versions will give a result:

| S_NO |     1 |     2 |   3 |    4 |
|   id |     1 |     5 |   8 |    9 |
| atr1 |     a | esdds |   i |  ads |
| atr2 |  bsdf |     f |   j |  sdf |
| atr3 | csdfs |   gds | ksd | dfse |
| atr4 |   djk |  hkkj |  lk |  wer |
Taryn
  • 242,637
  • 56
  • 362
  • 405