简体   繁体   中英

how to convert row to column if unknown number of columns SQL server

I have data like this:

 MaGiangVienID  |  SoTiet1  |  SoTiet2  |  DateID
79000G07.000206 |      60   |    60.00  |     t11
79000G07.000206 |      54   |    54.00  |     t12

I want to my result like this:

 MaGiangVienID  | SoTiet1_t11 | SoTiet2_t11 | SoTiet1_t12 | SoTiet2_t12
79000G07.000206 |          60 |       60.00 |          54 |       54.00

I don't know how many columns because MaGiangVienID have a lot of DateID

Please help me!!Thanks a lot.

For this type of data transformation you need to apply both the UNPIVOT and PIVOT functions. The UNPIVOT takes the data from the multiple columns and places it into rows and then the PIVOT takes the rows and converts it back to columns.

To perform the UNPIVOT all of the values that you convert to rows must be the same datatype so conversion might be needed.

Unpivot :

select MaGiangVienID, 
    value, 
    col +'_'+DateId col
from
(
  select MaGiangVienID, 
    cast(SoTiet1 as varchar(50)) SoTiet1, 
    cast(SoTiet2 as varchar(50)) SoTiet2,
    DateID
  from yourtable
) src
unpivot
(
  value
  for col in (SoTiet1, SoTiet2)
) unpiv

See SQL Fiddle with Demo . The result of the unpivot is:

|   MAGIANGVIENID | VALUE |         COL |
-----------------------------------------
| 79000G07.000206 |    60 | SoTiet1_t11 |
| 79000G07.000206 | 60.00 | SoTiet2_t11 |
| 79000G07.000206 |    54 | SoTiet1_t12 |
| 79000G07.000206 | 54.00 | SoTiet2_t12 |

As you see the UNPIVOT generates the new column names with the DateId appended to the end of it. Now you apply the PIVOT function.

Static PIVOT:

select MaGiangVienID, SoTiet1_t11, SoTiet2_t11, SoTiet1_t12, SoTiet2_t12
from
(
  select MaGiangVienID, 
    value, 
    col +'_'+DateId col
  from
  (
    select MaGiangVienID, 
      cast(SoTiet1 as varchar(50)) SoTiet1, 
      cast(SoTiet2 as varchar(50)) SoTiet2,
      DateID
    from yourtable
  ) src
  unpivot
  (
    value
    for col in (SoTiet1, SoTiet2)
  ) unpiv
) src
pivot
(
  max(value)
  for col in (SoTiet1_t11, SoTiet2_t11, SoTiet1_t12, SoTiet2_t12)
) piv

See SQL Fiddle with Demo

Now the above version works great if you have a known number of DateId values but you stated that you don't so you will want to implement this same query using dynamic sql.

Dynamic PIVOT:

DECLARE @colsUnpivot AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @colsPivot as  NVARCHAR(MAX)

select @colsUnpivot = stuff((select ','+quotename(C.name)
         from sys.columns as C
         where C.object_id = object_id('yourtable') and
               C.name not in ('MaGiangVienID', 'DateID')
         for xml path('')), 1, 1, '')

select @colsPivot = STUFF((SELECT DISTINCT ',' 
                      + quotename(c.name + '_'+t.DateId)
                    from yourtable t
                    cross apply sys.columns as C
                   where C.object_id = object_id('yourtable') and
                         C.name not in ('MaGiangVienID', 'DateID')

            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query 
  = 'select MaGiangVienID, '+@colsPivot+'
      from
      (
          select MaGiangVienID, value, col +''_''+DateId col
          from
          (
            select MaGiangVienID, 
              cast(SoTiet1 as varchar(50)) SoTiet1, 
              cast(SoTiet2 as varchar(50)) SoTiet2,
              DateID
            from yourtable
          ) src
          unpivot
          (
            value
            for col in ('+@colsUnpivot+')
          ) unpiv
      ) src
      pivot
      (
        max(value)
        for col in ('+ @colspivot +')
      ) p'

exec(@query)

See SQL Fiddle with Demo

Both versions produce the same result:

The result is:

|   MAGIANGVIENID | SOTIET1_T11 | SOTIET2_T11 | SOTIET1_T12 | SOTIET2_T12 |
---------------------------------------------------------------------------
| 79000G07.000206 |          60 |       60.00 |          54 |       54.00 |

If you do not have access to the UNPIVOT / PIVOT functions then you can replicate the query. The UNPIVOT function can be replicated using a UNION ALL and the PIVOT can be produced using a CASE statement with an aggregate function:

select MaGiangVienID,
  max(case when col = 'SoTiet1_t11' then value end) SoTiet1_t11,
  max(case when col = 'SoTiet2_t11' then value end) SoTiet2_t11,
  max(case when col = 'SoTiet1_t12' then value end) SoTiet1_t12,
  max(case when col = 'SoTiet2_t12' then value end) SoTiet2_t12
from
(
  select MaGiangVienID, 'SoTiet1_t11' col, cast(SoTiet1 as varchar(50)) value
  from yourtable
  where DateID = 't11'
  union all
  select MaGiangVienID, 'SoTiet2_t11' col, cast(SoTiet2 as varchar(50)) value
  from yourtable
  where DateID = 't11'
  union all
  select MaGiangVienID, 'SoTiet1_t12' col, cast(SoTiet1 as varchar(50)) value
  from yourtable
  where DateID = 't12'
  union all
  select MaGiangVienID, 'SoTiet2_t12' col, cast(SoTiet2 as varchar(50)) value
  from yourtable
  where DateID = 't12'
) src
group by MaGiangVienID

See SQL Fiddle with Demo

All versions will produce identical results.

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