Question

I am working on a project where I have the below requirement

My source table is

PeriodID     PeriodName   ProductID   ProductName  Productvalue
--------     ----------   ---------   -----------  ------------
 10           Jan          100          A            15
 20           Feb          100          A            25
 30           Mar          100          A            35
 10           Jan          200          B            12
 20           Feb          200          B            14
 30           Mar          200          B            18
 10           Jan          300          C            22
 20           Feb          300          C            23
 30           Mar          300          C            38

I want the output of the table to be like this

Destination table

PeriodID     PeriodName    A_ID   A     B_ID   B     C_ID  C
--------     ----------    ----   --    ----   --    ----  --
 10           Jan          100    15    200    12    300   22
 20           Feb          100    25    200    14    300   23
 30           Mar          100    35    200    18    300   38

I am trying to use pivot and unpivot of sql server 2008, but I am not that familiar with the features of those operators. I know this requirement looks weird,but this is how I want my output result set.

Please help me in this, I am really in trouble from past few days to get over this.

Was it helpful?

Solution

The simplest way to get the result would be to use an aggregate function along with a CASE expression:

select 
  periodid,
  periodname,
  max(case when productname = 'A' then productid end) A_ID,
  max(case when productname = 'A' then Productvalue end) A,
  max(case when productname = 'B' then productid end) B_ID,
  max(case when productname = 'B' then Productvalue end) B,
  max(case when productname = 'C' then productid end) C_ID,
  max(case when productname = 'C' then Productvalue end) C
from yourtable
group by periodid, periodname
order by periodid;

See SQL Fiddle with Demo.

However, if you want to use the PIVOT function to get the result I would first unpivot the productid and productvalue column first, so you no longer have multiple columns but multiple rows of this data. There are several ways that you can unpivot the data, you can use the unpivot function or you can use CROSS APPLY with a UNION ALL or VALUES (if you are using SQL Server 2008+). Since you are using SQL Server 2008 R2, here is how you can use CROSS APPLY with VALUES to unpivot the multiple columns:

select periodid, periodname, 
  col = productname+col,
  value
from yourtable
cross apply
(
  values
    ('_ID', ProductID),
    ('', Productvalue)
) c (col, value)

See SQL Fiddle with Demo. This gets your data into the format:

| PERIODID | PERIODNAME |  COL | VALUE |
|----------|------------|------|-------|
|       10 |        Jan | A_ID |   100 |
|       10 |        Jan |    A |    15 |
|       20 |        Feb | A_ID |   100 |
|       20 |        Feb |    A |    25 |
|       30 |        Mar | A_ID |   100 |

Now you can easily apply the PIVOT function to this data:

select periodid, periodname,
  A_ID, A, B_ID, B, C_ID, C
from 
(
  select periodid, periodname, 
    col = productname+col,
    value
  from yourtable
  cross apply
  (
    values
      ('_ID', ProductID),
      ('', Productvalue)
  ) c (col, value)
) d
pivot
(
  max(value)
  for col in (A_ID, A, B_ID, B, C_ID, C)
) piv
order by periodid;

See SQL Fiddle with Demo.

Finally if you have an unknown number of product names, then you can use dynamic SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(productname+col) 
                    from yourtable
                    cross apply
                    (
                      select '_ID', 1 union all
                      select '', 2
                    ) c (col, so)
                    group by col, so, productname
                    order by productname, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT periodid, periodname,' + @cols + ' 
            from 
            (
                select periodid, periodname, 
                  col = productname+col,
                  value
                from yourtable
                cross apply
                (
                  values
                    (''_ID'', ProductID),
                    ('''', Productvalue)
                ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p 
            order by periodid'

execute sp_executesql @query;

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

| PERIODID | PERIODNAME | A_ID |  A | B_ID |  B | C_ID |  C |
|----------|------------|------|----|------|----|------|----|
|       10 |        Jan |  100 | 15 |  200 | 12 |  300 | 22 |
|       20 |        Feb |  100 | 25 |  200 | 14 |  300 | 23 |
|       30 |        Mar |  100 | 35 |  200 | 18 |  300 | 38 |

OTHER TIPS

If the combination PeriodID & ProductID is unique key, the following query will give you the result you wanted.

Do you want it?

 SELECT     PeriodID
      , MIN(PeriodName) AS PeriodName
      , MIN(A_ID) AS A_ID
      , MIN(A) AS A
      , MIN(B_ID) AS B_ID
      , MIN(B) AS B
      , MIN(C_ID) AS C_ID
      , MIN(C) AS C
 FROM (
    SELECT     PeriodID
             , PeriodName
             , CASE ProductID WHEN 'A' THEN ProductID
                              ELSE NULL
               END AS A_ID
             , CASE ProductID WHEN 'A' THEN Productvalue
                              ELSE NULL
               END AS A
             , CASE ProductID WHEN 'B' THEN ProductID
                              ELSE NULL
               END AS B_ID
             , CASE ProductID WHEN 'B' THEN Productvalue
                              ELSE NULL
               END AS B 
             , CASE ProductID WHEN 'C' THEN ProductID
                              ELSE NULL
               END AS C
             , CASE ProductID WHEN 'C' THEN Productvalue
                              ELSE NULL
               END AS C
      FROM     YOUR_TABLE
    ) 
    GROUP BY PeriodID
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top