SQL Server 2008 - 数据透视表

Smi*_*ley 1 pivot pivot-table sql-server-2008

我试图在我的表上做一个Pivot输出行作为列.我已经在互联网上看到了一些例子,但是每次解释到达聚合时我都迷失了(我认为这与我想要达到的目标无关?).我有以下类表:

StudentID       ClassCode
10001           ENG240
10001           MTH100
10001           BIO101
10001           HUM300
10002           PHY200
10002           PHY200-L
10002           MTH100
10002           HUM200
10002           CHR100
10002           COM140
10003           HUM100
10003           ENG200
10003           PHY101
Run Code Online (Sandbox Code Playgroud)

我想得到的是以下输出:

StudentID       ClassCode 1     ClassCode 2     ClassCode 3     ClassCode 4     ClassCode 5
10001           ENG240          MTH100          BIO101          HUM300
10002           PHY200          PHY200-L        MTH100          HUM200          CHR100
10002           COM140
10003           HUM100          ENG200          PHY101    
Run Code Online (Sandbox Code Playgroud)

枢轴字段最多只能是五列.如果有超过五个班级的学生,则应将新记录添加到结果集中.

任何人都可以请我指出一个很好的方法来实现这一目标吗?Thakns这么多!

*编辑:*

到目前为止,我可以使用下面的查询来旋转表格:

CREATE 
  TABLE  #TestClass 
         (StudentID INT, row INT, ClassCode VARCHAR(32))


  ;WITH  TCSPivot(StudentID, row, ClassCode)
     AS
        (SELECT  StudentID,
                 row_number() OVER(PARTITION BY StudentID ORDER BY StudentID, ClassCode),
                 ClassCode
           FROM  student_class
         )

 INSERT 
   INTO  #TestClass
 SELECT  p.StudentID, 
         p.row, 
         p.ClassCode
   FROM  MyPivot p
   JOIN  class c
     ON  c.ClassCode   = p.ClassCode


    SELECT  @sql = @sql + ', MAX(CASE WHEN row = ' + CAST(tc.row AS CHAR(5)) + ' THEN ClassCode ELSE '''' END) AS [ClassCode ' + CAST(tc.row AS CHAR(5)) + ']'
       FROM  #TestClass tc 
      GROUP 
         BY  tc.row
      ORDER  
         BY  tc.row

        SET  @sql = @sql + N' 
                            FROM  #TestClass
                           GROUP 
                              BY  StudentID
                           ORDER 
                              BY  StudentID'

       EXEC  sp_executesql @sql
Run Code Online (Sandbox Code Playgroud)

我现在需要做的是如何限制只有5条记录应该垂直旋转.如果存在类别大于5的StudentID,则应添加第二条记录.

感谢大家!!

Tar*_*ryn 7

您可以轻松地实现2个窗函数得到的结果,ntile()row_number().

NTILE()将用于将您的数据划分为"存储桶",因此在您使用时NTILE(5),您ClassCodes将为每个存储创建5个存储桶StudentId.

select StudentId, ClassCode,
  newCol =
   'ClassCode' +
      cast(ntile(5) over(partition by StudentId 
                         order by ClassCode) as varchar(1))
from TestClass;
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo.这将使您的数据格式为:

| STUDENTID | CLASSCODE |     NEWCOL |
|-----------|-----------|------------|
|     10002 |    CHR100 | ClassCode1 |
|     10002 |    COM140 | ClassCode1 |
|     10002 |    HUM200 | ClassCode2 |
|     10002 |    MTH100 | ClassCode3 |
|     10002 |    PHY200 | ClassCode4 |
|     10002 |  PHY200-L | ClassCode5 |
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,数据现在位于5个桶中,这些桶是您的新列名ClassCode1,ClassCode2等等.您还会注意到有两行ClassCode1,如果您应用PIVOT函数,现在只返回一行.要返回多行,您需要应用于row_number()数据.

row_number()会为每一行数据的唯一序列:

;with cte as
(
  select StudentId, ClassCode,
    newCol =
      'ClassCode' +
        cast(ntile(5) over(partition by StudentId 
                           order by ClassCode) as varchar(1))
  from TestClass
),
mr as
(
  select StudentId, ClassCode,
    newCol,
    row_number() over(partition by StudentId, newCol order by newCol) seq
  from cte
)
select *
from mr;
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo.这得到了以下结果:

| STUDENTID | CLASSCODE |     NEWCOL | SEQ |
|-----------|-----------|------------|-----|
|     10002 |    CHR100 | ClassCode1 |   1 |
|     10002 |    COM140 | ClassCode1 |   2 |
|     10002 |    HUM200 | ClassCode2 |   1 |
|     10002 |    MTH100 | ClassCode3 |   1 |
|     10002 |    PHY200 | ClassCode4 |   1 |
|     10002 |  PHY200-L | ClassCode5 |   1 |
Run Code Online (Sandbox Code Playgroud)

现在NewCol相同的值ClassCode1具有不同的序列号.在数据透视过程中按数据分组时需要这样做.

最后,您可以应用该PIVOT函数来获得最终结果:

;with cte as
(
  select StudentId, ClassCode,
    newCol =
      'ClassCode' +
        cast(ntile(5) over(partition by StudentId 
                           order by ClassCode) as varchar(1))
  from TestClass
),
mr as
(
  select StudentId, ClassCode,
    newCol,
    row_number() over(partition by StudentId, newCol order by newCol) seq
  from cte
)
select studentid,
  ClassCode1, ClassCode2, ClassCode3, 
  ClassCode4, ClassCode5
from  mr
pivot
(
  max(ClassCode)
  for NewCol in (ClassCode1, ClassCode2, ClassCode3, 
                 ClassCode4, ClassCode5)
) piv
order by StudentId;
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo.

如果你想像你在问题中那样使用带有CASE表达式的聚合函数,那么你仍然会使用NTILE(),row_number()但最终的代码是:

;with cte as
(
  select StudentId, ClassCode,
    newCol =
      'ClassCode' +
        cast(ntile(5) over(partition by StudentId 
                           order by ClassCode) as varchar(1))
  from TestClass
),
mr as
(
  select StudentId, ClassCode,
    newCol,
    row_number() over(partition by StudentId, newCol order by newCol) seq
  from cte
)
select studentid,
  max(case when newcol = 'ClassCode1' then ClassCode end) ClassCode1,
  max(case when newcol = 'ClassCode2' then ClassCode end) ClassCode2,
  max(case when newcol = 'ClassCode3' then ClassCode end) ClassCode3,
  max(case when newcol = 'ClassCode4' then ClassCode end) ClassCode4,
  max(case when newcol = 'ClassCode5' then ClassCode end) ClassCode5
from  mr
group by StudentId, seq
order by StudentId;
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo.两个版本都将给出最终结果:

| STUDENTID | CLASSCODE1 | CLASSCODE2 | CLASSCODE3 | CLASSCODE4 | CLASSCODE5 |
|-----------|------------|------------|------------|------------|------------|
|     10001 |     BIO101 |     ENG240 |     HUM300 |     MTH100 |     (null) |
|     10002 |     CHR100 |     HUM200 |     MTH100 |     PHY200 |   PHY200-L |
|     10002 |     COM140 |     (null) |     (null) |     (null) |     (null) |
|     10003 |     ENG200 |     HUM100 |     PHY101 |     (null) |     (null) |
Run Code Online (Sandbox Code Playgroud)

由于您只有5列,因此不需要使用动态SQL来获取结果.