6



我在 SQL Server 中遇到交叉表查询问题。

假设我有如下数据:

| ScoreID | StudentID |      Name |    Sex | SubjectName | Score |
------------------------------------------------------------------
|       1 |         1 | Student A |   Male |           C |   100 |
|       2 |         1 | Student A |   Male |         C++ |    40 |
|       3 |         1 | Student A |   Male |     English |    60 |
|       4 |         1 | Student A |   Male |    Database |    15 |
|       5 |         1 | Student A |   Male |        Math |    50 |
|       6 |         2 | Student B |   Male |           C |    77 |
|       7 |         2 | Student B |   Male |         C++ |    12 |
|       8 |         2 | Student B |   Male |     English |    56 |
|       9 |         2 | Student B |   Male |    Database |    34 |
|      10 |         2 | Student B |   Male |        Math |    76 |
|      11 |         3 | Student C | Female |           C |    24 |
|      12 |         3 | Student C | Female |         C++ |    10 |
|      13 |         3 | Student C | Female |     English |    15 |
|      14 |         3 | Student C | Female |    Database |    40 |
|      15 |         3 | Student C | Female |        Math |    21 |
|      16 |         4 | Student D | Female |           C |    17 |
|      17 |         4 | Student D | Female |         C++ |    34 |
|      18 |         4 | Student D | Female |     English |    24 |
|      19 |         4 | Student D | Female |    Database |    56 |
|      20 |         4 | Student D | Female |        Math |    43 |

我想进行查询,结果如下:

| StuID| Name      | Sex    | C  | C++ | Eng | DB | Math | Total | Average |
|  1   | Student A | Male   | 100|  40 | 60  | 15 |  50  |  265  |   54    |
|  2   | Student B | Male   | 77 |  12 | 56  | 34 |  76  |  255  |   51    |
|  3   | Student C | Female | 24 |  10 | 15  | 40 |  21  |  110  |   22    |
|  4   | Student D | Female | 17 |  34 | 24  | 56 |  43  |  174  |   34.8  |

我如何查询以显示这样的输出?

笔记:

主题名称:

  • C
  • C++
  • 英语
  • 数据库
  • 数学

    将根据学生学习的科目而改变。

请访问http://sqlfiddle.com/#!6/2ba07/1测试此查询。

4

3 回答 3

17

有两种方法可以执行PIVOT静态(硬编码值)和动态(在执行时确定列)。

即使您想要一个动态版本,但有时从静态版本开始PIVOT然后转向动态版​​本更容易。

静态版本:

SELECT studentid, name, sex,[C], [C++], [English], [Database], [Math], total, average
from 
(
  select s1.studentid, name, sex, subjectname, score, total, average
  from Score s1
  inner join
  (
    select studentid, sum(score) total, avg(score) average
    from score
    group by studentid
  ) s2
    on s1.studentid = s2.studentid
) x
pivot 
(
   min(score)
   for subjectname in ([C], [C++], [English], [Database], [Math])
) p

请参阅带有演示的 SQL Fiddle

现在,如果您不知道将要转换的值,那么您可以为此使用动态 SQL:

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

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(SubjectName) 
                    from Score
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')



set @query = 'SELECT studentid, name, sex,' + @cols + ', total, average
              from 
             (
                select s1.studentid, name, sex, subjectname, score, total, average
                from Score s1
                inner join
                (
                  select studentid, sum(score) total, avg(score) average
                  from score
                  group by studentid
                ) s2
                  on s1.studentid = s2.studentid
            ) x
            pivot 
            (
                min(score)
                for subjectname in (' + @cols + ')
            ) p '

execute(@query)

请参阅带有演示的 SQL Fiddle

两个版本将产生相同的结果。

只是为了完善答案,如果您没有PIVOT函数,则可以使用CASE聚合函数获得此结果:

select s1.studentid, name, sex, 
  min(case when subjectname = 'C' then score end) C,
  min(case when subjectname = 'C++' then score end) [C++],
  min(case when subjectname = 'English' then score end) English,
  min(case when subjectname = 'Database' then score end) [Database],
  min(case when subjectname = 'Math' then score end) Math,
  total, average
from Score s1
inner join
(
  select studentid, sum(score) total, avg(score) average
  from score
  group by studentid
) s2
  on s1.studentid = s2.studentid
group by s1.studentid, name, sex, total, average

请参阅带有演示的 SQL Fiddle

于 2012-09-14T10:53:59.433 回答
1

在这种情况下,您需要使用 SQL PIVOT。请参考以下链接:

以未知列数为中心

在 SQL Server 中透视两个或更多列

SQL Server 中具有动态列的数据透视

于 2012-09-14T10:52:35.927 回答
0

这需要在运行时构建 SQL 查询字符串。SQL Server 中的列名、计数和数据类型始终是静态的(最重要的原因是优化器在优化时必须知道查询数据流)。

所以我建议你PIVOT在运行时构建一个 -query 并通过sp_executesql. 请注意,您必须对枢轴列值进行硬编码。小心正确地逃脱它们。您不能为它们使用参数。

或者,您可以为每个列数构建一个这样的查询,并将参数仅用于枢轴值。您必须分配一些虚拟列名,例如Pivot0, Pivot1, .... 对于每列计数,您仍然需要一个查询模板。除非您愿意将最大数量的枢轴列硬编码到查询中(比如 20)。在这种情况下,您实际上可以使用静态 SQL。

于 2012-09-14T10:49:55.943 回答