简体   繁体   中英

Pivoting Multiple Columns Based On a Single Column

I tried following several other suggestions on this website but coulnd't make this work for some reason.

Basically I'm trying to get from this table of Tasks:

| TaskID | ProjectID | TaskType | TaskDate1 | TaskDate2 |
---------------------------------------------------------
|      1 |         1 |    Type4 |  20130401 |  20130506 |
|      2 |         1 |    Type0 |  20130412 |  20130508 |
|      3 |         1 |    Type2 |  20130420 |  20130517 |

To that one:

| ProjectID | Type0Date1 | Type0Date2 | Type2Date1 | Type2Date2 | Type4Date1 | Type4Date2 |
---------------------------------------------------------
|         1 |   20130412 |   20130508 |   20130420 |   20130517 |   20130401 |   20130506 |

Each project has to have each of several predefined tasks (identified by their type). These tasks have a lot of properties but I'm interested in 2 dates and it would be nice to bring them all in one row per project.

I can easily pivot it for one of the columns with: PIVOT (MAX(TaskDate1) FOR TaskType IN ([Type0],[Type2],[Type4]))

I tried duplicating the TaskType column and doing another pivot around it for TaskDate2 but that won't give me a single row.

I also can't wrap my head around how unpivoting the table first would get me where I need to be, as was the suggestion in some other posts for similar questions.

If there is a scalable way of doing this (as in pivoting even more columns) it would be nice to know.

Thanks.

You can get the desired result but since you need to PIVOT on multiple columns ( TaskDate1 and TaskDate2 ) I would first unpivot the two columns into multiple rows.

Since you are using SQL Server 2008+, you can use either the UNPIVOT function or CROSS APPLY to convert the multiple columns. The basic syntax will be:

select projectid,
  col = TaskType+col,
  value
from yourtable
cross apply
(
  values 
    ('Date1', TaskDate1),
    ('Date2', TaskDate2)
) c(col, value)

See SQL Fiddle with Demo . This will convert your data into an easy to use format:

| PROJECTID |        COL |                        VALUE |
|-----------|------------|------------------------------|
|         1 | Type4Date1 | April, 01 2013 00:00:00+0000 |
|         1 | Type4Date2 |   May, 06 2013 00:00:00+0000 |
|         1 | Type0Date1 | April, 12 2013 00:00:00+0000 |
|         1 | Type0Date2 |   May, 08 2013 00:00:00+0000 |

Once you have the data in this format, then you can apply the PIVOT function to the get the max(value) for each col :

select projectId,
  Type0Date1, Type0Date2, 
  Type2Date1, Type2Date2,
  Type4Date1, Type4Date2
from
(
  select projectid,
    col = TaskType+col,
    value
  from yourtable
  cross apply
  (
    values 
      ('Date1', TaskDate1),
      ('Date2', TaskDate2)
  ) c(col, value)
) d
pivot
(
  max(value)
  for col in (Type0Date1, Type0Date2, Type2Date1, Type2Date2,
              Type4Date1, Type4Date2)
) piv;

See SQL Fiddle with Demo .

The above version will work great if you have a limited number of values per ProjectId , but if you have an unknown number of Tasks for each project, then you will need to use dynamic SQL to get the final result:

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

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

set @query = 'SELECT projectid, ' + @cols + ' 
            from 
            (
              select projectid,
                col = TaskType+col,
                value
              from yourtable
              cross apply
              (
                values 
                  (''Date1'', TaskDate1),
                  (''Date2'', TaskDate2)
              ) c(col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query

See SQL Fiddle with Demo . These will give a result:

| PROJECTID |                   TYPE0DATE1 |                 TYPE0DATE2 |                   TYPE2DATE1 |                 TYPE2DATE2 |                   TYPE4DATE1 |                 TYPE4DATE2 |
|-----------|------------------------------|----------------------------|------------------------------|----------------------------|------------------------------|----------------------------|
|         1 | April, 12 2013 00:00:00+0000 | May, 08 2013 00:00:00+0000 | April, 20 2013 00:00:00+0000 | May, 17 2013 00:00:00+0000 | April, 01 2013 00:00:00+0000 | May, 06 2013 00:00:00+0000 |

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