SQL Pivoting或Transposing或...列到行?

时间:2013-08-19 16:11:48

标签: sql sql-server tsql pivot

我有一个问题,这在SQLfiddle中看起来更好: http://www.sqlfiddle.com/#!3/dffa1/2

我为每个用户提供了一个包含多行数据的表格,其中包含日期戳和测试结果,我想将其转置或转换为一行结果,如下所示:每个用户都列出了所有时间和值结果:

USERID PSA1_time PSA1_result PSA2_time PSA2_result PSA3_time PSA3_result ...


1      1999-.... 2           1998...   4           1999...   6

3      1992...   4           1994      6

4      2006 ...  8

下表:

CREATE TABLE yourtable
    ([userid] int, [Ranking] int,[test] varchar(3), [Date] datetime, [result] int)
; 

INSERT INTO yourtable
    ([userid], [Ranking],[test], [Date], [result])
VALUES
    ('1', '1', 'PSA', 1997-05-20, 2),
    ('1', '2','PSA', 1998-05-07, 4),
    ('1', '3','PSA', 1999-06-08, 6),
    ('1', '4','PSA', 2001-06-08, 8),
    ('1', '5','PSA', 2004-06-08, 0),
        ('3', '1','PSA', 1992-05-07, 4),
    ('3', '2','PSA', 1994-06-08, 6),
    ('4', '1','PSA', 2006-06-08, 8)
;

1 个答案:

答案 0 :(得分:0)

由于你想要PIVOT两列,我的建议是首先忽略dateresult列,然后应用PIVOT函数。

unpivot流程会将两列dateresult转换为多行:

select userid, 
  col = test +'_'+cast(ranking as varchar(10))+'_'+ col,
  value
from yourtable t1
cross apply
(
  select 'time', convert(varchar(10), date, 120) union all
  select 'result', cast(result as varchar(10))
) c (col, value)

Demo。这会给你一个结果:

| USERID |          COL |      VALUE |
--------------------------------------
|      1 |   PSA_1_time | 1997-05-20 |
|      1 | PSA_1_result |          2 |
|      1 |   PSA_2_time | 1998-05-07 |
|      1 | PSA_2_result |          4 |
|      1 |   PSA_3_time | 1999-06-08 |

既然您拥有此格式的数据,那么您可以应用数据透视表来获取max中每个项目的min / col值:

如果您的列数有限,则可以对查询进行硬编码:

select *
from
(
  select userid, 
    col = test +'_'+cast(ranking as varchar(10))+'_'+ col,
    value
  from yourtable t1
  cross apply
  (
    select 'time', convert(varchar(10), date, 120) union all
    select 'result', cast(result as varchar(10))
  ) c (col, value)
) d
pivot
(
  max(value)
  for col in (PSA_1_time, PSA_1_result,
              PSA_2_time, PSA_2_result,
              PSA_3_time, PSA_3_result,
              PSA_4_time, PSA_4_result,
              PSA_5_time, PSA_5_result)
) piv;

请参阅SQL Fiddle with Demo

如果您有未知的列,那么您将需要使用动态SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(test +'_'+cast(ranking as varchar(10))+'_'+ col) 
                    from yourtable
                    cross apply
                    (
                      select 'time', 1 union all
                      select 'result', 2
                    ) c (col, so)
                    group by test, ranking, col, so
                    order by Ranking, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT userid,' + @cols + ' 
            from 
            (
              select userid, 
                col = test +''_''+cast(ranking as varchar(10))+''_''+ col,
                value
              from yourtable t1
              cross apply
              (
                select ''time'', convert(varchar(10), date, 120) union all
                select ''result'', cast(result as varchar(10))
              ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query;

SQL Fiddle with Demo。两个版本都会给出结果:

| USERID | PSA_1_TIME | PSA_1_RESULT | PSA_2_TIME | PSA_2_RESULT | PSA_3_TIME | PSA_3_RESULT | PSA_4_TIME | PSA_4_RESULT | PSA_5_TIME | PSA_5_RESULT |
------------------------------------------------------------------------------------------------------------------------------------------------------
|      1 | 1997-05-20 |            2 | 1998-05-07 |            4 | 1999-06-08 |            6 | 2001-06-08 |            8 | 2004-06-08 |            0 |
|      3 | 1992-05-07 |            4 | 1994-06-08 |            6 |     (null) |       (null) |     (null) |       (null) |     (null) |       (null) |
|      4 | 2006-06-08 |            8 |     (null) |       (null) |     (null) |       (null) |     (null) |       (null) |     (null) |       (null) |